daalainfo.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. /* daalainfo - based on Ogginfo
  2. *
  3. * A tool to describe ogg file contents and metadata.
  4. *
  5. * Copyright 2002-2005 Michael Smith <msmith@xiph.org>
  6. * Licensed under the GNU GPL, distributed with this program.
  7. */
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <errno.h>
  11. #include <string.h>
  12. #include <stdarg.h>
  13. #include <getopt.h>
  14. #include <math.h>
  15. #include <ogg/ogg.h>
  16. #include "../include/daala/codec.h"
  17. #include "../include/daala/daaladec.h"
  18. #define CHUNK 4500
  19. #ifdef _WIN32
  20. #define I64FORMAT "I64d"
  21. #else
  22. #define I64FORMAT PRId64
  23. #endif
  24. /* TODO:
  25. *
  26. * - detect violations of muxing constraints
  27. * - detect granulepos 'gaps' (possibly vorbis-specific). (seperate from
  28. * serial-number gaps)
  29. */
  30. typedef struct _stream_processor {
  31. void (*process_page)(struct _stream_processor *, ogg_page *);
  32. void (*process_end)(struct _stream_processor *);
  33. int isillegal;
  34. int constraint_violated;
  35. int shownillegal;
  36. int isnew;
  37. long seqno;
  38. int lostseq;
  39. int start;
  40. int end;
  41. int num;
  42. char *type;
  43. ogg_uint32_t serial; /* must be 32 bit unsigned */
  44. ogg_stream_state os;
  45. void *data;
  46. } stream_processor;
  47. typedef struct {
  48. stream_processor *streams;
  49. int allocated;
  50. int used;
  51. int in_headers;
  52. } stream_set;
  53. typedef struct {
  54. daala_info di;
  55. daala_comment dc;
  56. daala_setup_info *ds;
  57. ogg_int64_t bytes;
  58. ogg_int64_t lastgranulepos;
  59. ogg_int64_t firstgranulepos;
  60. int doneheaders;
  61. ogg_int64_t framenum_expected;
  62. } misc_daala_info;
  63. #define CONSTRAINT_PAGE_AFTER_EOS 1
  64. #define CONSTRAINT_MUXING_VIOLATED 2
  65. static stream_set *create_stream_set(void) {
  66. stream_set *set = calloc(1, sizeof(stream_set));
  67. set->streams = calloc(5, sizeof(stream_processor));
  68. set->allocated = 5;
  69. set->used = 0;
  70. return set;
  71. }
  72. static void check_xiph_comment(stream_processor *stream, int i,
  73. const char *comment, int comment_length) {
  74. char *sep;
  75. int j;
  76. int broken;
  77. unsigned char *val;
  78. int bytes;
  79. int remaining;
  80. broken = 0;
  81. sep = strchr(comment, '=');
  82. if (sep == NULL) {
  83. fprintf(stderr, "WARNING: Comment %d in stream %d has invalid format, "
  84. "does not contain '=': \"%s\"\n", i, stream->num, comment);
  85. return;
  86. }
  87. for (j = 0; j < sep - comment; j++) {
  88. if (comment[j] < 0x20 || comment[j] > 0x7D) {
  89. fprintf(stderr, "WARNING: Invalid comment fieldname in "
  90. "comment %d (stream %d): \"%s\"\n", i, stream->num, comment);
  91. broken = 1;
  92. break;
  93. }
  94. }
  95. if (broken) {
  96. return;
  97. }
  98. val = (unsigned char *)comment;
  99. j = sep - comment + 1;
  100. while (j < comment_length) {
  101. remaining = comment_length - j;
  102. if ((val[j] & 0x80) == 0) {
  103. bytes = 1;
  104. }
  105. else if ((val[j] & 0x40) == 0x40) {
  106. if ((val[j] & 0x20) == 0) {
  107. bytes = 2;
  108. }
  109. else if ((val[j] & 0x10) == 0) {
  110. bytes = 3;
  111. }
  112. else if((val[j] & 0x08) == 0) {
  113. bytes = 4;
  114. }
  115. else if((val[j] & 0x04) == 0) {
  116. bytes = 5;
  117. }
  118. else if((val[j] & 0x02) == 0) {
  119. bytes = 6;
  120. }
  121. else {
  122. fprintf(stderr, "WARNING: Illegal UTF-8 sequence in "
  123. "comment %d (stream %d): length marker wrong\n",
  124. i, stream->num);
  125. broken = 1;
  126. break;
  127. }
  128. }
  129. else {
  130. fprintf(stderr, "WARNING: Illegal UTF-8 sequence in comment "
  131. "%d (stream %d): length marker wrong\n", i, stream->num);
  132. broken = 1;
  133. break;
  134. }
  135. if(bytes > remaining) {
  136. fprintf(stderr, "WARNING: Illegal UTF-8 sequence in comment "
  137. "%d (stream %d): too few bytes\n", i, stream->num);
  138. broken = 1;
  139. break;
  140. }
  141. switch (bytes) {
  142. case 1:
  143. /* No more checks needed */
  144. break;
  145. case 2:
  146. if ((val[j+1] & 0xC0) != 0x80) {
  147. broken = 1;
  148. }
  149. if ((val[j] & 0xFE) == 0xC0) {
  150. broken = 1;
  151. }
  152. break;
  153. case 3:
  154. if(!((val[j] == 0xE0 && val[j+1] >= 0xA0 && val[j+1] <= 0xBF &&
  155. (val[j+2] & 0xC0) == 0x80) ||
  156. (val[j] >= 0xE1 && val[j] <= 0xEC &&
  157. (val[j+1] & 0xC0) == 0x80 &&
  158. (val[j+2] & 0xC0) == 0x80) ||
  159. (val[j] == 0xED && val[j+1] >= 0x80 &&
  160. val[j+1] <= 0x9F &&
  161. (val[j+2] & 0xC0) == 0x80) ||
  162. (val[j] >= 0xEE && val[j] <= 0xEF &&
  163. (val[j+1] & 0xC0) == 0x80 &&
  164. (val[j+2] & 0xC0) == 0x80))) {
  165. broken = 1;
  166. }
  167. if (val[j] == 0xE0 && (val[j+1] & 0xE0) == 0x80) {
  168. broken = 1;
  169. }
  170. break;
  171. case 4:
  172. if (!((val[j] == 0xF0 && val[j+1] >= 0x90 &&
  173. val[j+1] <= 0xBF &&
  174. (val[j+2] & 0xC0) == 0x80 &&
  175. (val[j+3] & 0xC0) == 0x80) ||
  176. (val[j] >= 0xF1 && val[j] <= 0xF3 &&
  177. (val[j+1] & 0xC0) == 0x80 &&
  178. (val[j+2] & 0xC0) == 0x80 &&
  179. (val[j+3] & 0xC0) == 0x80) ||
  180. (val[j] == 0xF4 && val[j+1] >= 0x80 &&
  181. val[j+1] <= 0x8F &&
  182. (val[j+2] & 0xC0) == 0x80 &&
  183. (val[j+3] & 0xC0) == 0x80))) {
  184. broken = 1;
  185. }
  186. if (val[j] == 0xF0 && (val[j+1] & 0xF0) == 0x80) {
  187. broken = 1;
  188. }
  189. break;
  190. /* 5 and 6 aren't actually allowed at this point */
  191. case 5:
  192. broken = 1;
  193. break;
  194. case 6:
  195. broken = 1;
  196. break;
  197. }
  198. if (broken) {
  199. char *simple = malloc(comment_length + 1);
  200. char *seq = malloc(comment_length * 3 + 1);
  201. static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  202. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  203. int c, c1 = 0, c2 = 0;
  204. for (c = 0; c < comment_length; c++) {
  205. seq[c1++] = hex[((unsigned char)comment[i]) >> 4];
  206. seq[c1++] = hex[((unsigned char)comment[i]) & 0xf];
  207. seq[c1++] = ' ';
  208. if (comment[i] < 0x20 || comment[i] > 0x7D) {
  209. simple[c2++] = '?';
  210. }
  211. else {
  212. simple[c2++] = comment[i];
  213. }
  214. }
  215. seq[c1] = 0;
  216. simple[c2] = 0;
  217. fprintf(stderr, "WARNING: Illegal UTF-8 sequence in comment "
  218. "%d (stream %d): invalid sequence \"%s\": %s\n", i,
  219. stream->num, simple, seq);
  220. broken = 1;
  221. free(simple);
  222. free(seq);
  223. break;
  224. }
  225. j += bytes;
  226. }
  227. }
  228. static void ogg_to_daala_packet(daala_packet *dp, ogg_packet *op) {
  229. dp->packet = op->packet;
  230. dp->bytes = op->bytes;
  231. dp->b_o_s = op->b_o_s;
  232. dp->e_o_s = op->e_o_s;
  233. dp->granulepos = op->granulepos;
  234. dp->packetno = op->packetno;
  235. }
  236. static void daala_process(stream_processor *stream, ogg_page *page) {
  237. daala_packet dp;
  238. ogg_packet op;
  239. misc_daala_info *inf;
  240. int i, header=0;
  241. int res;
  242. inf = stream->data;
  243. ogg_stream_pagein(&stream->os, page);
  244. if (inf->doneheaders < 3) {
  245. header = 1;
  246. }
  247. while(1) {
  248. res = ogg_stream_packetout(&stream->os, &op);
  249. if (res < 0) {
  250. fprintf(stderr, "WARNING: discontinuity in stream (%d)\n", stream->num);
  251. continue;
  252. }
  253. else if (res == 0) {
  254. break;
  255. }
  256. ogg_to_daala_packet(&dp, &op);
  257. if (inf->doneheaders < 3) {
  258. if (daala_decode_header_in(&inf->di, &inf->dc, &inf->ds, &dp) < 0) {
  259. fprintf(stderr, "WARNING: Could not decode Daala header packet - "
  260. "invalid Daala stream (%d)\n", stream->num);
  261. continue;
  262. }
  263. inf->doneheaders++;
  264. if (inf->doneheaders == 3) {
  265. if (ogg_page_granulepos(page) != 0
  266. || ogg_stream_packetpeek(&stream->os, NULL) == 1) {
  267. fprintf(stderr, "WARNING: Daala stream %d does not have headers "
  268. "correctly framed. Terminal header page contains "
  269. "additional packets or has non-zero granulepos\n",
  270. stream->num);
  271. }
  272. printf("Daala headers parsed for stream %d, "
  273. "information follows...\n", stream->num);
  274. printf("Version: %d.%d.%d\n", inf->di.version_major,
  275. inf->di.version_minor, inf->di.version_sub);
  276. printf("Vendor: %s\n", inf->dc.vendor);
  277. printf("Width: %d\n", inf->di.pic_width);
  278. printf("Height: %d\n", inf->di.pic_height);
  279. if (inf->di.timebase_numerator == 0
  280. || inf->di.timebase_denominator == 0) {
  281. fprintf(stderr, "Invalid zero framerate\n");
  282. }
  283. else {
  284. printf("Framerate %d/%d (%.02f fps)\n",
  285. inf->di.timebase_numerator, inf->di.timebase_denominator,
  286. (float)inf->di.timebase_numerator/
  287. (float)inf->di.timebase_denominator);
  288. }
  289. if(inf->di.pixel_aspect_numerator == 0
  290. || inf->di.pixel_aspect_denominator == 0) {
  291. printf("Aspect ratio undefined\n");
  292. }
  293. else {
  294. float frameaspect;
  295. frameaspect = (float)inf->di.pic_width/(float)inf->di.pic_height*
  296. (float)inf->di.pixel_aspect_numerator/
  297. (float)inf->di.pixel_aspect_denominator;
  298. printf("Pixel aspect ratio %d:%d (%f:1)\n",
  299. inf->di.pixel_aspect_numerator, inf->di.pixel_aspect_denominator,
  300. (float)inf->di.pixel_aspect_numerator/
  301. (float)inf->di.pixel_aspect_denominator);
  302. if (fabs(frameaspect - 4.0/3.0) < 0.02) {
  303. printf("Frame aspect 4:3\n");
  304. }
  305. else if(fabs(frameaspect - 16.0/9.0) < 0.02) {
  306. printf("Frame aspect 16:9\n");
  307. }
  308. else {
  309. printf("Frame aspect %f:1\n", frameaspect);
  310. }
  311. }
  312. if (inf->dc.comments > 0) {
  313. printf("User comments section follows...\n");
  314. }
  315. for (i = 0; i < inf->dc.comments; i++) {
  316. char *comment;
  317. comment = inf->dc.user_comments[i];
  318. check_xiph_comment(stream, i, comment, inf->dc.comment_lengths[i]);
  319. }
  320. }
  321. }
  322. else {
  323. ogg_int64_t framenum;
  324. ogg_int64_t iframe, pframe;
  325. ogg_int64_t gp;
  326. gp = op.granulepos;
  327. if (gp > 0) {
  328. iframe = gp >> inf->di.keyframe_granule_shift;
  329. pframe = gp - (iframe << inf->di.keyframe_granule_shift);
  330. framenum = iframe + pframe;
  331. if (inf->framenum_expected >= 0
  332. && inf->framenum_expected != framenum) {
  333. fprintf(stderr, "WARNING: Expected frame %" I64FORMAT
  334. ", got %" I64FORMAT "\n", inf->framenum_expected,
  335. framenum);
  336. }
  337. inf->framenum_expected = framenum + 1;
  338. }
  339. else if (inf->framenum_expected >= 0) {
  340. inf->framenum_expected++;
  341. }
  342. }
  343. }
  344. if (!header) {
  345. ogg_int64_t gp;
  346. gp = ogg_page_granulepos(page);
  347. if (gp > 0) {
  348. if (gp < inf->lastgranulepos) {
  349. fprintf(stderr, "WARNING: granulepos in stream %d decreases from %"
  350. I64FORMAT " to %" I64FORMAT "\n", stream->num, inf->lastgranulepos,
  351. gp);
  352. }
  353. inf->lastgranulepos = gp;
  354. }
  355. if (inf->firstgranulepos < 0) { /* Not set yet */
  356. }
  357. inf->bytes += page->header_len + page->body_len;
  358. }
  359. }
  360. static void daala_end(stream_processor *stream) {
  361. misc_daala_info *inf;
  362. long minutes, seconds, milliseconds;
  363. double bitrate, time;
  364. ogg_int64_t iframe, pframe;
  365. int new_gp;
  366. inf = stream->data;
  367. new_gp = inf->di.version_major > 3
  368. || (inf->di.version_major == 3 && (inf->di.version_minor > 2
  369. || (inf->di.version_minor == 2 && inf->di.version_sub > 0)));
  370. /* This should be lastgranulepos - startgranulepos, or something like that*/
  371. iframe = inf->lastgranulepos >> inf->di.keyframe_granule_shift;
  372. pframe = inf->lastgranulepos - (iframe << inf->di.keyframe_granule_shift);
  373. /* The granule position starts at 0 for stream version 3.2.0, but starts at
  374. 1 for version 3.2.1 and above. In the former case, we need to add one
  375. to the final granule position to get the frame count. */
  376. time = (double)(iframe + pframe + !new_gp)/
  377. ((float)inf->di.timebase_numerator/(float)inf->di.timebase_denominator);
  378. minutes = (long)time/60;
  379. seconds = (long)time - minutes*60;
  380. milliseconds = (long)((time - minutes*60 - seconds)*1000);
  381. bitrate = inf->bytes*8/time/1000.0;
  382. printf("Daala stream %d:\n"
  383. "\tTotal data length: %" I64FORMAT " bytes\n"
  384. "\tPlayback length: %ldm:%02ld.%03lds\n"
  385. "\tAverage bitrate: %f kb/s\n",
  386. stream->num, inf->bytes, minutes, seconds, milliseconds, bitrate);
  387. daala_comment_clear(&inf->dc);
  388. daala_info_clear(&inf->di);
  389. daala_setup_free(inf->ds);
  390. free(stream->data);
  391. }
  392. static void process_null(stream_processor *stream, ogg_page *page) {
  393. /* This is for invalid streams. */
  394. (void)stream;
  395. (void)page;
  396. }
  397. static void process_other(stream_processor *stream, ogg_page *page ) {
  398. ogg_packet op;
  399. ogg_stream_pagein(&stream->os, page);
  400. while (ogg_stream_packetout(&stream->os, &op) > 0) {
  401. /* Should we do anything here? Currently, we don't */
  402. }
  403. }
  404. static void free_stream_set(stream_set *set) {
  405. int i;
  406. for (i = 0; i < set->used; i++) {
  407. if (!set->streams[i].end) {
  408. fprintf(stderr, "WARNING: EOS not set on stream %d\n",
  409. set->streams[i].num);
  410. if (set->streams[i].process_end) {
  411. set->streams[i].process_end(&set->streams[i]);
  412. }
  413. }
  414. ogg_stream_clear(&set->streams[i].os);
  415. }
  416. free(set->streams);
  417. free(set);
  418. }
  419. static int streams_open(stream_set *set) {
  420. int i;
  421. int res;
  422. res = 0;
  423. for (i = 0; i < set->used; i++) {
  424. if (!set->streams[i].end) {
  425. res++;
  426. }
  427. }
  428. return res;
  429. }
  430. static void null_start(stream_processor *stream) {
  431. stream->process_end = NULL;
  432. stream->type = "invalid";
  433. stream->process_page = process_null;
  434. }
  435. static void other_start(stream_processor *stream, char *type) {
  436. if (type) {
  437. stream->type = type;
  438. }
  439. else {
  440. stream->type = "unknown";
  441. }
  442. stream->process_page = process_other;
  443. stream->process_end = NULL;
  444. }
  445. static void daala_start(stream_processor *stream) {
  446. misc_daala_info *info;
  447. stream->type = "daala";
  448. stream->process_page = daala_process;
  449. stream->process_end = daala_end;
  450. stream->data = calloc(1, sizeof(misc_daala_info));
  451. info = stream->data;
  452. info->framenum_expected = -1;
  453. }
  454. static stream_processor *find_stream_processor(stream_set *set,
  455. ogg_page *page) {
  456. ogg_uint32_t serial;
  457. int i, invalid, constraint;
  458. stream_processor *stream;
  459. serial = ogg_page_serialno(page);
  460. invalid = constraint = 0;
  461. for (i = 0; i < set->used; i++) {
  462. if (serial == set->streams[i].serial) {
  463. /* We have a match! */
  464. stream = &(set->streams[i]);
  465. set->in_headers = 0;
  466. /* if we have detected EOS, then this can't occur here. */
  467. if (stream->end) {
  468. stream->isillegal = 1;
  469. stream->constraint_violated = CONSTRAINT_PAGE_AFTER_EOS;
  470. return stream;
  471. }
  472. stream->isnew = 0;
  473. stream->start = ogg_page_bos(page);
  474. stream->end = ogg_page_eos(page);
  475. stream->serial = serial;
  476. return stream;
  477. }
  478. }
  479. /* If there are streams open, and we've reached the end of the
  480. * headers, then we can't be starting a new stream.
  481. * XXX: might this sometimes catch ok streams if EOS flag is missing,
  482. * but the stream is otherwise ok?
  483. */
  484. if (streams_open(set) && !set->in_headers) {
  485. constraint = CONSTRAINT_MUXING_VIOLATED;
  486. invalid = 1;
  487. }
  488. set->in_headers = 1;
  489. if (set->allocated < set->used) {
  490. stream = &set->streams[set->used];
  491. }
  492. else {
  493. set->allocated += 5;
  494. set->streams = realloc(set->streams, sizeof(stream_processor)*
  495. set->allocated);
  496. stream = &set->streams[set->used];
  497. }
  498. set->used++;
  499. stream->num = set->used; /* We count from 1 */
  500. stream->isnew = 1;
  501. stream->isillegal = invalid;
  502. stream->constraint_violated = constraint;
  503. {
  504. int res;
  505. ogg_packet op;
  506. /* We end up processing the header page twice, but that's ok. */
  507. ogg_stream_init(&stream->os, serial);
  508. ogg_stream_pagein(&stream->os, page);
  509. res = ogg_stream_packetout(&stream->os, &op);
  510. if (res <= 0) {
  511. fprintf(stderr, "WARNING: Invalid header page, no packet found\n");
  512. null_start(stream);
  513. }
  514. else if (op.bytes >= 6
  515. && memcmp(op.packet, "\x80""daala", 6) == 0) {
  516. daala_start(stream);
  517. }
  518. else {
  519. other_start(stream, NULL);
  520. }
  521. res = ogg_stream_packetout(&stream->os, &op);
  522. if (res > 0) {
  523. fprintf(stderr, "WARNING: Invalid header page in stream %d, "
  524. "contains multiple packets\n", stream->num);
  525. }
  526. /* re-init, ready for processing */
  527. ogg_stream_clear(&stream->os);
  528. ogg_stream_init(&stream->os, serial);
  529. }
  530. stream->start = ogg_page_bos(page);
  531. stream->end = ogg_page_eos(page);
  532. stream->serial = serial;
  533. if (stream->serial == 0 || stream->serial == UINT32_MAX) {
  534. printf("Note: Stream %d has serial number %d, which is legal but may "
  535. "cause problems with some tools.\n", stream->num, stream->serial);
  536. }
  537. return stream;
  538. }
  539. static int get_next_page(FILE *f, ogg_sync_state *sync, ogg_page *page,
  540. ogg_int64_t *written) {
  541. int ret;
  542. char *buffer;
  543. int bytes;
  544. while ((ret = ogg_sync_pageseek(sync, page)) <= 0) {
  545. if (ret < 0) {
  546. /* unsynced, we jump over bytes to a possible capture - we don't need to
  547. read more just yet */
  548. fprintf(stderr, "WARNING: Hole in data (%d bytes) found at approximate "
  549. "offset %" I64FORMAT " bytes. Corrupted Ogg.\n", -ret, *written);
  550. continue;
  551. }
  552. /* zero return, we didn't have enough data to find a whole page, read */
  553. buffer = ogg_sync_buffer(sync, CHUNK);
  554. bytes = fread(buffer, 1, CHUNK, f);
  555. if (bytes <= 0) {
  556. ogg_sync_wrote(sync, 0);
  557. return 0;
  558. }
  559. ogg_sync_wrote(sync, bytes);
  560. *written += bytes;
  561. }
  562. return 1;
  563. }
  564. static void process_file(char *filename) {
  565. FILE *file;
  566. ogg_sync_state sync;
  567. ogg_page page;
  568. stream_set *processors;
  569. int gotpage;
  570. ogg_int64_t written;
  571. file = fopen(filename, "rb");
  572. processors = create_stream_set();
  573. written = 0;
  574. gotpage = 0;
  575. if (!file) {
  576. fprintf(stderr, "Error opening input file \"%s\": %s\n", filename,
  577. strerror(errno));
  578. return;
  579. }
  580. printf("Processing file \"%s\"...\n\n", filename);
  581. ogg_sync_init(&sync);
  582. while (get_next_page(file, &sync, &page, &written)) {
  583. stream_processor *p;
  584. p = find_stream_processor(processors, &page);
  585. gotpage = 1;
  586. if (!p) {
  587. fprintf(stderr, "Could not find a processor for stream, bailing\n");
  588. return;
  589. }
  590. if (p->isillegal && !p->shownillegal) {
  591. char *constraint;
  592. switch (p->constraint_violated) {
  593. case CONSTRAINT_PAGE_AFTER_EOS:
  594. constraint = "Page found for stream after EOS flag";
  595. break;
  596. case CONSTRAINT_MUXING_VIOLATED:
  597. constraint = "Ogg muxing constraints violated, new "
  598. "stream before EOS of all previous streams";
  599. break;
  600. default:
  601. constraint = "Error unknown.";
  602. }
  603. fprintf(stderr, "WARNING: illegally placed page(s) for logical stream "
  604. "%d\nThis indicates a corrupt Ogg file: %s.\n", p->num, constraint);
  605. p->shownillegal = 1;
  606. /* If it's a new stream, we want to continue processing this page
  607. * anyway to suppress additional spurious errors
  608. */
  609. if (!p->isnew)
  610. continue;
  611. }
  612. if (p->isnew) {
  613. printf("New logical stream (#%d, serial: %08x): type %s\n",
  614. p->num, p->serial, p->type);
  615. if (!p->start) {
  616. fprintf(stderr, "WARNING: stream start flag not set on stream %d\n",
  617. p->num);
  618. }
  619. }
  620. else if(p->start) {
  621. fprintf(stderr, "WARNING: stream start flag found in mid-stream "
  622. "on stream %d\n", p->num);
  623. }
  624. if (p->seqno++ != ogg_page_pageno(&page)) {
  625. if (!p->lostseq) {
  626. fprintf(stderr, "WARNING: sequence number gap in stream %d. Got page "
  627. "%ld when expecting page %ld. Indicates missing data.\n",
  628. p->num, ogg_page_pageno(&page), p->seqno - 1);
  629. }
  630. p->seqno = ogg_page_pageno(&page);
  631. p->lostseq = 1;
  632. }
  633. else {
  634. p->lostseq = 0;
  635. }
  636. if (!p->isillegal) {
  637. p->process_page(p, &page);
  638. if (p->end) {
  639. if (p->process_end) {
  640. p->process_end(p);
  641. }
  642. printf("Logical stream %d ended\n", p->num);
  643. p->isillegal = 1;
  644. p->constraint_violated = CONSTRAINT_PAGE_AFTER_EOS;
  645. }
  646. }
  647. }
  648. if (!gotpage) {
  649. fprintf(stderr, "ERROR: No Ogg data found in file \"%s\".\n"
  650. "Input probably not Ogg.\n", filename);
  651. }
  652. free_stream_set(processors);
  653. ogg_sync_clear(&sync);
  654. fclose(file);
  655. }
  656. static void version(void) {
  657. printf("daalainfo from %s\n", daala_version_string());
  658. }
  659. static void usage(void) {
  660. version();
  661. printf(" by the Xiph.Org Foundation (http://www.xiph.org/)\n\n");
  662. printf("Usage: daalainfo [flags] file1.ogg [file2.ogx ... fileN.ogv]\n"
  663. "Flags supported:\n"
  664. "\t-h Show this help message\n");
  665. printf("\t-V Output version information and exit\n");
  666. }
  667. int main(int argc, char **argv) {
  668. int f, ret;
  669. if (argc < 2) {
  670. printf("Usage: daalainfo [flags] file1.ogg [file2.ogx ... fileN.ogv]\n"
  671. "\n"
  672. "daalainfo is a tool for printing information about Ogg Daala files\n"
  673. "and for diagnosing problems with them.\n"
  674. "Full help shown with \"daalainfo -h\".\n");
  675. return 1;
  676. }
  677. while ((ret = getopt(argc, argv, "hqvV")) >= 0) {
  678. switch (ret) {
  679. case 'h':
  680. usage();
  681. return 0;
  682. case 'V':
  683. version();
  684. return 0;
  685. }
  686. }
  687. if (optind >= argc) {
  688. fprintf(stderr, "No input files specified. \"daalainfo -h\" for help\n");
  689. return 1;
  690. }
  691. ret = 0;
  692. for (f = optind; f < argc; f++) {
  693. process_file(argv[f]);
  694. }
  695. return ret;
  696. }