dump_video.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*Daala video codec
  2. Copyright (c) 2006-2010 Daala project contributors. All rights reserved.
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. - Redistributions of source code must retain the above copyright notice, this
  6. list of conditions and the following disclaimer.
  7. - Redistributions in binary form must reproduce the above copyright notice,
  8. this list of conditions and the following disclaimer in the documentation
  9. and/or other materials provided with the distribution.
  10. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
  11. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  12. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  13. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  14. FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  15. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  16. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  17. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  18. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  19. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
  20. /*This code was cargo-culted from libtheora's dump_video.c.*/
  21. #ifdef HAVE_CONFIG_H
  22. # include "config.h"
  23. #endif
  24. #if !defined(_REENTRANT)
  25. # define _REENTRANT
  26. #endif
  27. #if !defined(_GNU_SOURCE)
  28. # define _GNU_SOURCE
  29. #endif
  30. #if !defined(_LARGEFILE_SOURCE)
  31. # define _LARGEFILE_SOURCE
  32. #endif
  33. #if !defined(_LARGEFILE64_SOURCE)
  34. # define _LARGEFILE64_SOURCE
  35. #endif
  36. #if !defined(_FILE_OFFSET_BITS)
  37. # define _FILE_OFFSET_BITS 64
  38. #endif
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. /*Yes, yes, we're going to hell.*/
  43. #if defined(_WIN32)
  44. # include <io.h>
  45. # include <fcntl.h>
  46. #endif
  47. #include <limits.h>
  48. #include <math.h>
  49. #include <signal.h>
  50. #include "getopt.h"
  51. #include "../src/logging.h"
  52. #include "../include/daala/daaladec.h"
  53. const char *optstring = "o:r";
  54. struct option options [] = {
  55. { "output", required_argument, NULL, 'o' },
  56. { "raw", no_argument, NULL, 'r' }, /*Disable YUV4MPEG2 headers:*/
  57. { "version", no_argument, NULL, 0},
  58. { NULL, 0, NULL, 0 }
  59. };
  60. /* Helper; just grab some more compressed bitstream and sync it for
  61. page extraction */
  62. int buffer_data(FILE *in, ogg_sync_state *oy) {
  63. char *buffer = ogg_sync_buffer(oy, 4096);
  64. int bytes = fread(buffer, 1, 4096, in);
  65. ogg_sync_wrote(oy, bytes);
  66. return bytes;
  67. }
  68. ogg_sync_state oy;
  69. ogg_page og;
  70. ogg_stream_state vo;
  71. ogg_stream_state to;
  72. daala_info di;
  73. daala_comment dc;
  74. daala_setup_info *ds;
  75. daala_dec_ctx *dd;
  76. od_img img;
  77. int daala_p = 0;
  78. int daala_processing_headers;
  79. int stateflag = 0;
  80. /* single frame video buffering */
  81. int videobuf_ready = 0;
  82. int raw = 0;
  83. FILE *outfile = NULL;
  84. int got_sigint = 0;
  85. static void sigint_handler(int signal) {
  86. (void)signal;
  87. got_sigint = 1;
  88. }
  89. /*Write out the planar YUV frame, uncropped.*/
  90. static void video_write(void) {
  91. int pli;
  92. int i;
  93. if (outfile) {
  94. if (!raw) fprintf(outfile, "FRAME\n");
  95. for (pli = 0; pli < img.nplanes; pli++) {
  96. int plane_width;
  97. int plane_height;
  98. int xdec;
  99. int ydec;
  100. xdec = img.planes[pli].xdec;
  101. ydec = img.planes[pli].ydec;
  102. plane_width = (img.width + (1 << xdec) - 1) >> xdec;
  103. plane_height = (img.height + (1 << ydec) - 1) >> ydec;
  104. for (i = 0; i < plane_height; i++) {
  105. if (fwrite(img.planes[pli].data + img.planes[pli].ystride*i, 1,
  106. plane_width, outfile) < (size_t)plane_width) {
  107. fprintf(stderr, "Error writing yuv frame");
  108. return;
  109. }
  110. }
  111. }
  112. }
  113. }
  114. /* dump the daala comment header */
  115. static int dump_comments(daala_comment *comment) {
  116. int i;
  117. int len;
  118. FILE *out;
  119. out = stderr;
  120. fprintf(out, "Encoded by %s\n", comment->vendor);
  121. if (comment->comments) {
  122. fprintf(out, "daala comment header:\n");
  123. for (i = 0; i < comment->comments; i++) {
  124. if (comment->user_comments[i]) {
  125. len = comment->comment_lengths[i] < INT_MAX ?
  126. comment->comment_lengths[i] : INT_MAX;
  127. fprintf(out, "\t%.*s\n", len, comment->user_comments[i]);
  128. }
  129. }
  130. }
  131. return 0;
  132. }
  133. /* helper: push a page into the appropriate steam */
  134. /* this can be done blindly; a stream won't accept a page
  135. that doesn't belong to it */
  136. static int queue_page(ogg_page *page) {
  137. if (daala_p) ogg_stream_pagein(&to, page);
  138. return 0;
  139. }
  140. static void usage(void) {
  141. fprintf(stderr,
  142. "Usage: dumpvid [options] [<file.ogv>] [-o outfile.y4m]\n"
  143. "If no input file is given, stdin is used.\n"
  144. "Options:\n\n"
  145. " -o --output <outfile.y4m> File name for decoded output. If\n"
  146. " this option is not given, the\n"
  147. " decompressed data is sent to stdout.\n"
  148. " -r --raw Output raw YUV with no framing instead\n"
  149. " of YUV4MPEG2 (the default).\n"
  150. " --version Displays version information.\n");
  151. exit(EXIT_FAILURE);
  152. }
  153. static void version(void) {
  154. fprintf(stderr, "%s\n", PACKAGE_STRING);
  155. exit(EXIT_SUCCESS);
  156. }
  157. int main(int argc, char *argv[]) {
  158. ogg_packet op;
  159. int long_option_index;
  160. int c;
  161. int frames = 0;
  162. int pix_fmt = 1;
  163. ogg_int32_t pic_width = 0;
  164. ogg_int32_t pic_height = 0;
  165. ogg_int32_t fps_num = 0;
  166. ogg_int32_t fps_denom = 0;
  167. FILE *infile = stdin;
  168. outfile = stdout;
  169. daala_log_init();
  170. #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
  171. /* Beware the evil ifdef. We avoid these where we can, but this one we
  172. cannot. Don't add any more, you'll probably go to hell if you do. */
  173. _setmode(_fileno(stdin), _O_BINARY);
  174. _setmode(_fileno(stdout), _O_BINARY);
  175. #endif
  176. /* Process option arguments. */
  177. while ((c = getopt_long(argc, argv, optstring, options, &long_option_index))
  178. != EOF) {
  179. switch (c) {
  180. case 'o': {
  181. if (strcmp(optarg, "-") != 0) {
  182. outfile = fopen(optarg, "wb");
  183. if (outfile == NULL) {
  184. fprintf(stderr, "Unable to open output file '%s'\n", optarg);
  185. exit(1);
  186. }
  187. }
  188. else {
  189. outfile = stdout;
  190. }
  191. break;
  192. }
  193. case 'r': {
  194. raw = 1;
  195. break;
  196. }
  197. case 0: {
  198. if (strcmp(options[long_option_index].name, "version") == 0) {
  199. version();
  200. }
  201. break;
  202. }
  203. default: usage(); break;
  204. }
  205. }
  206. if (optind < argc) {
  207. infile = fopen(argv[optind], "rb");
  208. if (infile == NULL) {
  209. fprintf(stderr, "Unable to open '%s' for extraction.\n", argv[optind]);
  210. exit(1);
  211. }
  212. if (++optind < argc) {
  213. usage();
  214. exit(1);
  215. }
  216. }
  217. /*Ok, Ogg parsing.
  218. The idea here is we have a bitstream that is made up of Ogg pages.
  219. The libogg sync layer will find them for us.
  220. There may be pages from several logical streams interleaved; we find the
  221. first daala stream and ignore any others.
  222. Then we pass the pages for our stream to the libogg stream layer which
  223. assembles our original set of packets out of them.
  224. start up Ogg stream synchronization layer */
  225. ogg_sync_init(&oy);
  226. /* init supporting Theora structures needed in header parsing */
  227. daala_comment_init(&dc);
  228. daala_info_init(&di);
  229. /*Ogg file open; parse the headers.
  230. Theora (like Vorbis) depends on some initial header packets for decoder
  231. setup and initialization.
  232. We retrieve these first before entering the main decode loop.*/
  233. /* Only interested in Daala streams */
  234. while (!stateflag) {
  235. int ret = buffer_data(infile, &oy);
  236. if (ret == 0) break;
  237. while (ogg_sync_pageout(&oy, &og) > 0) {
  238. int got_packet;
  239. ogg_stream_state test;
  240. /* is this a mandated initial header? If not, stop parsing */
  241. if (!ogg_page_bos(&og)) {
  242. /* don't leak the page; get it into the appropriate stream */
  243. queue_page(&og);
  244. stateflag = 1;
  245. break;
  246. }
  247. ogg_stream_init(&test, ogg_page_serialno(&og));
  248. ogg_stream_pagein(&test, &og);
  249. got_packet = ogg_stream_packetpeek(&test, &op);
  250. /* identify the codec: try daala */
  251. if ((got_packet == 1) && !daala_p && (daala_processing_headers =
  252. daala_decode_header_in(&di, &dc, &ds, &op)) >= 0) {
  253. /* it is daala -- save this stream state */
  254. memcpy(&to, &test, sizeof(test));
  255. daala_p = 1;
  256. /*Advance past the successfully processed header.*/
  257. if (daala_processing_headers) ogg_stream_packetout(&to, NULL);
  258. }
  259. else {
  260. /* whatever it is, we don't care about it */
  261. ogg_stream_clear(&test);
  262. }
  263. }
  264. /* fall through to non-bos page parsing */
  265. }
  266. /* we're expecting more header packets. */
  267. while (daala_p && daala_processing_headers) {
  268. int ret;
  269. /* look for further daala headers */
  270. while (daala_processing_headers &&
  271. (ret = ogg_stream_packetpeek(&to, &op))) {
  272. if (ret < 0) continue;
  273. daala_processing_headers = daala_decode_header_in(&di, &dc, &ds, &op);
  274. if (daala_processing_headers < 0) {
  275. fprintf(stderr, "Error parsing Daala stream headers; "
  276. "corrupt stream?\n");
  277. exit(1);
  278. }
  279. else if (daala_processing_headers >= 0) {
  280. /*Advance past the successfully processed header.*/
  281. ogg_stream_packetout(&to, NULL);
  282. }
  283. daala_p++;
  284. }
  285. /*Stop now so we don't fail if there aren't enough pages in a short
  286. stream.*/
  287. if (!(daala_p && daala_processing_headers)) break;
  288. /* The header pages/packets will arrive before anything else we
  289. care about, or the stream is not obeying spec */
  290. if (ogg_sync_pageout(&oy, &og) > 0) {
  291. queue_page(&og); /* demux into the appropriate stream */
  292. }
  293. else {
  294. if (buffer_data(infile, &oy) == 0) { /* someone needs more data */
  295. fprintf(stderr, "End of file while searching for codec headers.\n");
  296. exit(1);
  297. }
  298. }
  299. }
  300. /* and now we have it all. initialize decoders */
  301. if (daala_p) {
  302. dump_comments(&dc);
  303. dd = daala_decode_alloc(&di, ds);
  304. fprintf(stderr, "Ogg logical stream %lx is Daala %dx%d %.02f fps video\n",
  305. to.serialno, di.pic_width, di.pic_height,
  306. di.timebase_numerator/(double)di.timebase_denominator*di.frame_duration);
  307. }
  308. else {
  309. /* tear down the partial daala setup */
  310. daala_info_clear(&di);
  311. daala_comment_clear(&dc);
  312. }
  313. /*Either way, we're done with the codec setup data.*/
  314. daala_setup_free(ds);
  315. if (!raw && outfile) {
  316. static const char *CHROMA_TYPES[4] = { "420jpeg", NULL, "422jpeg", "444" };
  317. pic_width = di.pic_width;
  318. pic_height = di.pic_height;
  319. fps_num = di.timebase_numerator;
  320. fps_denom = di.timebase_denominator*di.frame_duration;
  321. /*calculate pixel_fmt based on the xdec & ydec values from one of the
  322. chroma planes.*/
  323. if (di.plane_info[1].xdec == 1 && di.plane_info[1].ydec == 1) {
  324. pix_fmt = 0;
  325. }
  326. else if (di.plane_info[1].xdec == 1 && di.plane_info[1].ydec == 0) {
  327. pix_fmt = 2;
  328. }
  329. else if (di.plane_info[1].xdec == 0 && di.plane_info[1].ydec == 0) {
  330. pix_fmt = 3;
  331. }
  332. if (pix_fmt >= 4 || pix_fmt == 1) {
  333. fprintf(stderr, "Unknown pixel format: %i\n", pix_fmt);
  334. exit(1);
  335. }
  336. /*Store header information*/
  337. fprintf(outfile, "YUV4MPEG2 W%d H%d F%d:%d Ip A%d:%d C%s\n",
  338. pic_width, pic_height, fps_num, fps_denom,
  339. di.pixel_aspect_numerator, di.pixel_aspect_denominator,
  340. CHROMA_TYPES[pix_fmt]);
  341. }
  342. /* install signal handler */
  343. signal(SIGINT, sigint_handler);
  344. /*Finally the main decode loop.
  345. It's one Daala packet per frame, so this is pretty straightforward if
  346. we're not trying to maintain sync with other multiplexed streams.
  347. The videobuf_ready flag is used to maintain the input buffer in the libogg
  348. stream state.
  349. If there's no output frame available at the end of the decode step, we must
  350. need more input data.
  351. We could simplify this by just using the return code on
  352. ogg_page_packetout(), but the flag system extends easily to the case where
  353. you care about more than one multiplexed stream (like with audio
  354. playback).
  355. In that case, just maintain a flag for each decoder you care about, and
  356. pull data when any one of them stalls.*/
  357. stateflag = 0; /* playback has not begun */
  358. /* queue any remaining pages from data we buffered but that did not
  359. contain headers */
  360. while (ogg_sync_pageout(&oy, &og) > 0) {
  361. queue_page(&og);
  362. }
  363. while (!got_sigint) {
  364. while (daala_p && !videobuf_ready) {
  365. if (ogg_stream_packetout(&to, &op) > 0) {
  366. if (daala_decode_packet_in(dd, &img, &op) >= 0) {
  367. videobuf_ready = 1;
  368. frames++;
  369. }
  370. }
  371. else break;
  372. }
  373. if (!videobuf_ready && feof(infile)) break;
  374. if (!videobuf_ready) {
  375. /* no data yet for somebody. Grab another page */
  376. buffer_data(infile, &oy);
  377. while (ogg_sync_pageout(&oy, &og) > 0) {
  378. queue_page(&og);
  379. }
  380. }
  381. /* dumpvideo frame, and get new one */
  382. else if (outfile) video_write();
  383. videobuf_ready = 0;
  384. }
  385. /* end of decoder loop -- close everything */
  386. if (daala_p) {
  387. ogg_stream_clear(&to);
  388. daala_decode_free(dd);
  389. daala_comment_clear(&dc);
  390. daala_info_clear(&di);
  391. }
  392. ogg_sync_clear(&oy);
  393. if (infile && infile != stdin) fclose(infile);
  394. if (outfile && outfile != stdout) fclose(outfile);
  395. fprintf(stderr, "\n\n%d frames\n", frames);
  396. fprintf(stderr, "\nDone.\n");
  397. return 0;
  398. }