player_example.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /*Daala video codec
  2. Copyright (c) 2006-2013 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. #ifdef HAVE_CONFIG_H
  21. # include "config.h"
  22. #endif
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <daala/codec.h>
  26. #include <daala/daaladec.h>
  27. #include "SDL.h"
  28. #define ODS_NONE 0
  29. #define ODS_HEADER 1
  30. #define ODS_DATA 2
  31. typedef struct {
  32. SDL_Window *screen;
  33. SDL_Renderer *renderer;
  34. daala_info di;
  35. daala_comment dc;
  36. ogg_sync_state oy;
  37. FILE *input;
  38. const char *input_path;
  39. ogg_stream_state os;
  40. daala_dec_ctx *dctx;
  41. SDL_Texture *texture;
  42. od_img img;
  43. int width;
  44. int height;
  45. int done;
  46. int od_state;
  47. int paused;
  48. int restart;
  49. int slow;
  50. int loop;
  51. int step;
  52. int fullscreen;
  53. int valid;
  54. int plane_mask;
  55. } player_example;
  56. enum {
  57. OD_LUMA_MASK = 1 << 0,
  58. OD_CB_MASK = 1 << 1,
  59. OD_CR_MASK = 1 << 2,
  60. OD_ALL_MASK = OD_LUMA_MASK | OD_CB_MASK | OD_CR_MASK
  61. };
  62. static void img_to_rgb(SDL_Texture *tex, const od_img *img, int plane_mask);
  63. static int next_plane(int plane_mask);
  64. static void wait_to_refresh(uint32_t *previous_ticks, uint32_t ms_per_frame);
  65. int player_example_init(player_example *player);
  66. player_example *player_example_create();
  67. int player_example_clear(player_example *player);
  68. int player_example_free(player_example *player);
  69. int player_example_reset(player_example *player);
  70. int player_example_play(player_example *player);
  71. int player_example_restart(player_example *player);
  72. int player_example_open(player_example *player, char *path);
  73. int player_example_daala_stream_init(player_example *player, int serial) {
  74. int ret;
  75. if (player == NULL) return -1;
  76. ret = ogg_stream_init(&player->os, serial);
  77. if (ret != 0) return -1;
  78. daala_info_init(&player->di);
  79. daala_comment_init(&player->dc);
  80. player->od_state = ODS_HEADER;
  81. return 0;
  82. }
  83. int player_example_daala_stream_clear(player_example *player) {
  84. if (player == NULL) return -1;
  85. daala_info_clear(&player->di);
  86. daala_comment_clear(&player->dc);
  87. if (player->dctx != NULL) {
  88. daala_decode_free(player->dctx);
  89. player->dctx = NULL;
  90. }
  91. ogg_stream_clear(&player->os);
  92. player->od_state = ODS_NONE;
  93. return 0;
  94. }
  95. int player_example_init(player_example *player) {
  96. if (player == NULL) return -1;
  97. player->screen = NULL;
  98. player->renderer = NULL;
  99. player->texture = NULL;
  100. player->width = 0;
  101. player->height = 0;
  102. player->paused = 0;
  103. player->restart = 0;
  104. player->slow = 0;
  105. player->loop = 0;
  106. player->done = 0;
  107. player->step = 0;
  108. player->fullscreen = 0;
  109. player->valid = 0;
  110. player->od_state = ODS_NONE;
  111. player->plane_mask = OD_ALL_MASK;
  112. return 0;
  113. }
  114. int player_example_clear(player_example *player) {
  115. if (player == NULL) return -1;
  116. memset(player, 0, sizeof(player_example));
  117. return 0;
  118. }
  119. player_example *player_example_create() {
  120. int ret;
  121. player_example *player;
  122. player = (player_example *)malloc(sizeof(player_example));
  123. if (player == NULL) return NULL;
  124. ret = player_example_init(player);
  125. if (ret != 0) {
  126. free(player);
  127. return NULL;
  128. }
  129. return player;
  130. }
  131. int player_example_free(player_example *player) {
  132. int ret;
  133. if (player == NULL) return -1;
  134. ret = player_example_clear(player);
  135. if (ret == 0) {
  136. free(player);
  137. return 0;
  138. }
  139. return -1;
  140. }
  141. int player_example_reset(player_example *player) {
  142. int ret;
  143. if (player == NULL) return -1;
  144. ret = player_example_clear(player);
  145. if (ret != 0) return -1;
  146. ret = player_example_init(player);
  147. if (ret != 0) return -1;
  148. return 0;
  149. }
  150. int player_example_open_input(player_example *player, const char *path) {
  151. if ((player == NULL) || ((path == NULL) || (path[0] == '\0'))) return -1;
  152. if ((path[0] == '-') && (path[1] == '\0')) {
  153. player->input = stdin;
  154. }
  155. else {
  156. player->input = fopen(path, "rb");
  157. }
  158. if (player->input == NULL) {
  159. player->input_path = "";
  160. return -1;
  161. }
  162. player->input_path = path;
  163. ogg_sync_init(&player->oy);
  164. return 0;
  165. }
  166. int player_example_close_input(player_example *player) {
  167. int ret;
  168. if (player == NULL) return -1;
  169. if ((player->input == stdin) || (player->input == NULL)) return -1;
  170. ret = fclose(player->input);
  171. player->input = NULL;
  172. ogg_sync_clear(&player->oy);
  173. if (ret != 0) return -1;
  174. return 0;
  175. }
  176. int player_example_input_restart(player_example *player) {
  177. int ret;
  178. if (player == NULL) return -1;
  179. if (player->input == stdin) return -1;
  180. ret = player_example_close_input(player);
  181. if (ret != 0) return -1;
  182. ret = player_example_open_input(player, player->input_path);
  183. return ret;
  184. }
  185. void player_example_handle_event(player_example *player, SDL_Event *event) {
  186. switch (event->type) {
  187. case SDL_QUIT: {
  188. player->done = 1;
  189. break;
  190. }
  191. case SDL_KEYDOWN: {
  192. switch (event->key.keysym.sym) {
  193. case SDLK_q: {
  194. player->done = 1;
  195. break;
  196. }
  197. case SDLK_s: {
  198. player->slow = !player->slow;
  199. break;
  200. }
  201. case SDLK_p: {
  202. player->plane_mask = next_plane(player->plane_mask);
  203. break;
  204. }
  205. case SDLK_l: {
  206. player->loop = !player->loop;
  207. break;
  208. }
  209. case SDLK_ESCAPE: {
  210. player->done = 1;
  211. break;
  212. }
  213. case SDLK_SPACE: {
  214. player->paused = !player->paused;
  215. player->step = 0;
  216. break;
  217. }
  218. case SDLK_RIGHT:
  219. case SDLK_PERIOD: {
  220. player->step = 1;
  221. player->paused = 1;
  222. break;
  223. }
  224. case SDLK_HOME:
  225. case SDLK_r: {
  226. player->restart = 1;
  227. if (player->paused) {
  228. player->step = 1;
  229. }
  230. break;
  231. }
  232. case SDLK_f: {
  233. player->fullscreen = !player->fullscreen;
  234. SDL_SetWindowFullscreen(player->screen,
  235. player->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
  236. break;
  237. }
  238. default: break;
  239. }
  240. break;
  241. }
  242. }
  243. }
  244. void player_example_wait_user_input(player_example *player) {
  245. SDL_Event event;
  246. if (SDL_WaitEvent(&event)) {
  247. player_example_handle_event(player, &event);
  248. }
  249. }
  250. void player_example_check_user_input(player_example *player) {
  251. SDL_Event event;
  252. while (SDL_PollEvent(&event)) {
  253. player_example_handle_event(player, &event);
  254. }
  255. }
  256. int player_example_play(player_example *player) {
  257. size_t bytes;
  258. char *buffer;
  259. int ret;
  260. ogg_page page;
  261. ogg_packet packet;
  262. daala_setup_info *dsi;
  263. uint32_t ms_per_frame;
  264. uint32_t ticks = 0;
  265. ms_per_frame = 0;
  266. dsi = NULL;
  267. while (!player->done) {
  268. while (ogg_sync_pageout(&player->oy, &page) != 1) {
  269. buffer = ogg_sync_buffer(&player->oy, 4096);
  270. if (buffer == NULL) return -1;
  271. bytes = fread(buffer, 1, 4096, player->input);
  272. if (bytes > 0) {
  273. ret = ogg_sync_wrote(&player->oy, bytes);
  274. if (ret != 0) return -1;
  275. }
  276. else {
  277. if (!player->valid) {
  278. fprintf(stderr, "Invalid Ogg\n");
  279. exit(1);
  280. }
  281. if (player->od_state != ODS_NONE) {
  282. ret = player_example_daala_stream_clear(player);
  283. if (ret != 0) return -1;
  284. }
  285. if (player->input == stdin) {
  286. return 0;
  287. }
  288. if (player->loop == 1) {
  289. player_example_input_restart(player);
  290. continue;
  291. }
  292. for (;;) {
  293. player_example_wait_user_input(player);
  294. if (player->restart) {
  295. ret = player_example_input_restart(player);
  296. player->restart = 0;
  297. if (ret != 0) return -1;
  298. break;
  299. }
  300. if (player->done) {
  301. return 0;
  302. }
  303. }
  304. }
  305. }
  306. if (ogg_page_bos(&page)) {
  307. ret = player_example_daala_stream_init(player,
  308. ogg_page_serialno(&page));
  309. if (ret != 0) return -1;
  310. }
  311. ret = ogg_stream_pagein(&player->os, &page);
  312. if (ret != 0) return -1;
  313. while (ogg_stream_packetout(&player->os, &packet) == 1) {
  314. switch (player->od_state) {
  315. case ODS_HEADER: {
  316. ret =
  317. daala_decode_header_in(&player->di, &player->dc, &dsi, &packet);
  318. if (ret < 0) {
  319. if (memcmp(packet.packet, "fishead", packet.bytes)) {
  320. fprintf(stderr, "Ogg Skeleton streams not supported\n");
  321. }
  322. return -1;
  323. }
  324. if (ret != 0) break;
  325. player->dctx = daala_decode_alloc(&player->di, dsi);
  326. if (player->dctx == NULL) return -1;
  327. daala_setup_free(dsi);
  328. dsi = NULL;
  329. player->od_state = ODS_DATA;
  330. if (player->di.timebase_numerator
  331. && player->di.timebase_denominator) {
  332. ms_per_frame = 1000 /
  333. (player->di.timebase_numerator / player->di.timebase_denominator);
  334. ticks = SDL_GetTicks();
  335. }
  336. break;
  337. }
  338. case ODS_DATA: {
  339. if ((player->screen == NULL)
  340. || (player->width != player->di.pic_width)
  341. || (player->height != player->di.pic_height)) {
  342. player->width = player->di.pic_width;
  343. player->height = player->di.pic_height;
  344. player->screen = SDL_CreateWindow("Daala example player",
  345. SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  346. player->width, player->height,
  347. SDL_WINDOW_ALLOW_HIGHDPI);
  348. if (player->screen == NULL) return -1;
  349. player->renderer = SDL_CreateRenderer(player->screen, -1, 0);
  350. if (player->renderer == NULL) return -1;
  351. player->texture = SDL_CreateTexture(player->renderer,
  352. SDL_PIXELFORMAT_ARGB8888,
  353. SDL_TEXTUREACCESS_STREAMING,
  354. player->width, player->height);
  355. if (player->texture == NULL) return -1;
  356. }
  357. ret = daala_decode_packet_in(player->dctx, &player->img, &packet);
  358. if (ret != 0) return -1;
  359. player->valid = 1;
  360. if ((player->slow) && (!player->step)) {
  361. SDL_Delay(420);
  362. }
  363. player_example_check_user_input(player);
  364. while ((player->paused) && (!player->done)) {
  365. if (player->restart) {
  366. break;
  367. }
  368. if (player->step) {
  369. player->step = 0;
  370. break;
  371. }
  372. player_example_wait_user_input(player);
  373. }
  374. if ((!player->restart) && (!player->done)) {
  375. wait_to_refresh(&ticks, ms_per_frame);
  376. img_to_rgb(player->texture, &player->img, player->plane_mask);
  377. SDL_RenderClear(player->renderer);
  378. SDL_RenderCopy(player->renderer, player->texture, NULL, NULL);
  379. SDL_RenderPresent(player->renderer);
  380. }
  381. break;
  382. }
  383. }
  384. }
  385. if ((player->restart) || (ogg_page_eos(&page))) {
  386. ret = player_example_daala_stream_clear(player);
  387. if (ret != 0) return -1;
  388. }
  389. if (player->restart) {
  390. ret = player_example_input_restart(player);
  391. player->restart = 0;
  392. if (ret != 0) return -1;
  393. }
  394. }
  395. if (player->od_state != ODS_NONE) {
  396. ret = player_example_daala_stream_clear(player);
  397. if (ret != 0) return -1;
  398. }
  399. return 0;
  400. }
  401. int main(int argc, char *argv[]) {
  402. int ret;
  403. char *input;
  404. int start_paused;
  405. player_example *player;
  406. daala_log_init();
  407. if ((argc == 3) && (memcmp(argv[1], "-p", 2) == 0)) {
  408. start_paused = 1;
  409. input = argv[2];
  410. }
  411. else {
  412. if ((argc != 2) || ((argc == 2)
  413. && ((memcmp(argv[1], "-h", 2) == 0)
  414. || (memcmp(argv[1] + 1, "-h", 2) == 0)))) {
  415. fprintf(stderr, "usage: %s input.ogg\n%s\n", argv[0],
  416. "\nProgram Options:\n-p to start paused\n- to read from stdin\n\n"
  417. "Playback Control: \n"
  418. "r to restart\nl to loop\ns for slow\n. to step\nspace to pause\n"
  419. "p to switch planes\nq to quit");
  420. exit(1);
  421. } else if ((argc == 2)
  422. && memcmp(argv[1], "--version", 9) == 0
  423. && strlen(argv[1]) == strlen("--version")) {
  424. fprintf(stderr, "%s\n", PACKAGE_STRING);
  425. exit(0);
  426. }
  427. start_paused = 0;
  428. input = argv[1];
  429. }
  430. if (SDL_Init(SDL_INIT_VIDEO) < 0) {
  431. fprintf(stderr, "error: unable to init SDL\n");
  432. exit(1);
  433. }
  434. atexit(SDL_Quit);
  435. player = player_example_create();
  436. if (player == NULL) {
  437. fprintf(stderr, "player example error: create player\n");
  438. return -1;
  439. }
  440. ret = player_example_open_input(player, input);
  441. if (ret != 0) {
  442. fprintf(stderr, "player example error: could not open: %s\n", input);
  443. player_example_free(player);
  444. return -1;
  445. }
  446. if (start_paused == 1) {
  447. player->step = 1;
  448. player->paused = 1;
  449. }
  450. ret = player_example_play(player);
  451. if (ret != 0) {
  452. fprintf(stderr, "player example error: playback error\n");
  453. exit(1);
  454. }
  455. ret = player_example_free(player);
  456. return ret;
  457. }
  458. #define OD_MINI(a, b) ((a) < (b) ? (a) : (b))
  459. #define OD_MAXI(a, b) ((a) > (b) ? (a) : (b))
  460. #define OD_CLAMPI(a, b, c) (OD_MAXI(a, OD_MINI(b, c)))
  461. #define OD_SIGNMASK(a) (-((a) < 0))
  462. #define OD_FLIPSIGNI(a, b) (((a) + OD_SIGNMASK(b)) ^ OD_SIGNMASK(b))
  463. #define OD_DIV_ROUND(x, y) (((x) + OD_FLIPSIGNI((y) >> 1, x))/(y))
  464. #define OD_CLAMP255(x) \
  465. ((unsigned char)((((x) < 0) - 1) & ((x) | -((x) > 255))))
  466. void img_to_rgb(SDL_Texture *texture, const od_img *img, int plane_mask) {
  467. unsigned char *y_row;
  468. unsigned char *cb_row;
  469. unsigned char *cr_row;
  470. unsigned char *y;
  471. unsigned char *cb;
  472. unsigned char *cr;
  473. int y_stride;
  474. int cb_stride;
  475. int cr_stride;
  476. int width;
  477. int height;
  478. int xdec;
  479. int ydec;
  480. int i;
  481. int j;
  482. unsigned char *pixels;
  483. int pitch;
  484. /*Assume both C planes are decimated.*/
  485. xdec = img->planes[1].xdec;
  486. ydec = img->planes[1].ydec;
  487. y_stride = img->planes[0].ystride;
  488. cb_stride = img->planes[1].ystride;
  489. cr_stride = img->planes[2].ystride;
  490. y_row = img->planes[0].data;
  491. cb_row = img->planes[1].data;
  492. cr_row = img->planes[2].data;
  493. /*Lock the texture in video memory for update.*/
  494. if (SDL_LockTexture(texture, NULL, (void**)&pixels, &pitch)) {
  495. fprintf(stderr, "Couldn't lock video texture!");
  496. exit(1);
  497. }
  498. width = img->width;
  499. height = img->height;
  500. /*Chroma up-sampling is just done with a box filter.
  501. This is very likely what will actually be used in practice on a real
  502. display, and also removes one more layer to search in for the source of
  503. artifacts.
  504. As an added bonus, it's dead simple.*/
  505. for (j = 0; j < height; j++) {
  506. int dc;
  507. y = y_row;
  508. cb = cb_row;
  509. cr = cr_row;
  510. for (i = 0; i < 4 * width;) {
  511. int64_t yval;
  512. int64_t cbval;
  513. int64_t crval;
  514. unsigned rval;
  515. unsigned gval;
  516. unsigned bval;
  517. yval = (plane_mask & OD_LUMA_MASK) * (*y - 16)
  518. + (((plane_mask & OD_LUMA_MASK) ^ OD_LUMA_MASK) << 7);
  519. cbval = ((plane_mask & OD_CB_MASK) >> 1) * (*cb - 128);
  520. crval = ((plane_mask & OD_CR_MASK) >> 2) * (*cr - 128);
  521. /*This is intentionally slow and very accurate.*/
  522. rval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
  523. 2916394880000LL*yval + 4490222169144LL*crval, 9745792000LL), 65535);
  524. gval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
  525. 2916394880000LL*yval - 534117096223LL*cbval - 1334761232047LL*crval,
  526. 9745792000LL), 65535);
  527. bval = OD_CLAMPI(0, (int32_t)OD_DIV_ROUND(
  528. 2916394880000LL*yval + 5290866304968LL*cbval, 9745792000LL), 65535);
  529. *(pixels + pitch*j + i++) = (unsigned char)(bval >> 8);
  530. *(pixels + pitch*j + i++) = (unsigned char)(gval >> 8);
  531. *(pixels + pitch*j + i++) = (unsigned char)(rval >> 8);
  532. *(pixels + pitch*j + i++) = 0;
  533. dc = ((y - y_row) & 1) | (1 - xdec);
  534. y++;
  535. cb += dc;
  536. cr += dc;
  537. }
  538. y_row += y_stride;
  539. dc = -((j & 1) | (1 - ydec));
  540. cb_row += dc & cb_stride;
  541. cr_row += dc & cr_stride;
  542. }
  543. SDL_UnlockTexture(texture);
  544. }
  545. int next_plane(int plane_mask) {
  546. return OD_MINI(plane_mask << 1, OD_ALL_MASK) >>
  547. ((plane_mask == OD_ALL_MASK) << 1);
  548. }
  549. void wait_to_refresh(uint32_t *previous_ticks, uint32_t ms_per_frame)
  550. {
  551. uint32_t tmp;
  552. /* play dumb until we've parsed the frame rate */
  553. if (!*previous_ticks)
  554. return;
  555. tmp = SDL_GetTicks();
  556. while (tmp - *previous_ticks < ms_per_frame) {
  557. SDL_Delay(ms_per_frame - (tmp - *previous_ticks));
  558. tmp = SDL_GetTicks();
  559. }
  560. *previous_ticks = tmp;
  561. }