theora.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*
  2. * theora.h
  3. * https://gitlab.com/bztsrc/sdlogv
  4. *
  5. * Copyright (C) 2023 bzt (bztsrc@gitlab), MIT license
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
  20. * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  22. * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. *
  24. * @brief Multithreaded Vorbis / theora decoder header
  25. *
  26. */
  27. #include <stdint.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <SDL2/SDL.h>
  31. #include <SDL2/SDL_mixer.h> /* Mix_Chunk only */
  32. #include <SDL2/SDL_thread.h>
  33. #include <theora/theoradec.h>
  34. #include <vorbis/codec.h>
  35. /* using circular buffer must be thread safe, because there's only one producer and one consumer,
  36. * and head and tail are only changed by the producer and consumer respectively but never both */
  37. #define THEORA_QUEUE_SIZE 512
  38. typedef struct {
  39. uint32_t playms; /* when to play this frame in millisec */
  40. uint8_t *vbuf; /* data, in SDL_PIXELFORMAT_IYUV planar format */
  41. } theora_frame_t;
  42. typedef struct {
  43. /* public fields */
  44. volatile int hasAudio; /* has audio stream */
  45. volatile int hasVideo, w, h; /* has video stream, dimensions */
  46. /* private fields */
  47. volatile int started, stop, done; /* started, request stop to producer and acknowledge flag */
  48. volatile int ahead, atail, vhead, vtail; /* circular buffer pointers */
  49. volatile Mix_Chunk chunk[THEORA_QUEUE_SIZE]; /* audio buffer */
  50. volatile theora_frame_t frame[THEORA_QUEUE_SIZE]; /* video buffer */
  51. volatile Uint32 baseticks; /* decode start time to get video play time */
  52. SDL_Thread *th; /* thread identifier */
  53. FILE *f; /* file stream */
  54. } theora_t;
  55. /**
  56. * Get some data to parse. Rewrite this function if you don't like file streams
  57. */
  58. static int theora_getdata(theora_t *ctx, ogg_sync_state *oy)
  59. {
  60. char *buffer;
  61. int bytes;
  62. if(!ctx->f) return 0;
  63. buffer = ogg_sync_buffer(oy, 4096);
  64. bytes = fread(buffer, 1, 4096, ctx->f);
  65. if(bytes <= 0) return 0;
  66. return !ogg_sync_wrote(oy, bytes);
  67. }
  68. /**
  69. * Decoder and circular buffer producer
  70. */
  71. static int theora_producer(void *data)
  72. {
  73. theora_t *ctx = (theora_t *)data;
  74. uint8_t *dst, *p;
  75. int16_t *pcm, ival;
  76. uint32_t j, w, h;
  77. float **raw, val;
  78. int ret, i, doread, uvoff;
  79. ogg_int64_t videobuf_granulepos;
  80. ogg_packet op;
  81. ogg_sync_state oy;
  82. ogg_page og;
  83. ogg_stream_state vo;
  84. ogg_stream_state to;
  85. ogg_stream_state test;
  86. th_info ti;
  87. th_comment tc;
  88. th_dec_ctx *td;
  89. th_setup_info *ts;
  90. vorbis_info vi;
  91. vorbis_dsp_state vd;
  92. vorbis_block vb;
  93. vorbis_comment vc;
  94. th_ycbcr_buffer ycbcr;
  95. ogg_sync_init(&oy);
  96. vorbis_info_init(&vi);
  97. vorbis_comment_init(&vc);
  98. th_comment_init(&tc);
  99. th_info_init(&ti);
  100. td = NULL; ts = NULL;
  101. if(ctx->f) fseek(ctx->f, 0, SEEK_SET);
  102. /* Ogg file open; parse the headers */
  103. /* Only interested in Vorbis/Theora streams */
  104. ret = ctx->hasVideo = ctx->hasAudio = ctx->w = ctx->h = doread = 0;
  105. while (!ctx->stop && !ret) {
  106. if(!theora_getdata(ctx, &oy)) break;
  107. while(ogg_sync_pageout(&oy, &og) > 0) {
  108. if(!ogg_page_bos(&og)) {
  109. if(ctx->hasVideo) ogg_stream_pagein(&to, &og);
  110. if(ctx->hasAudio) ogg_stream_pagein(&vo, &og);
  111. ret = 1;
  112. break;
  113. }
  114. ogg_stream_init(&test, ogg_page_serialno(&og));
  115. ogg_stream_pagein(&test, &og);
  116. ogg_stream_packetout(&test, &op);
  117. if(!ctx->hasVideo && th_decode_headerin(&ti, &tc, &ts, &op) >= 0) {
  118. memcpy(&to, &test, sizeof(test));
  119. ctx->hasVideo = 1;
  120. } else
  121. if(!ctx->hasAudio && vorbis_synthesis_headerin(&vi, &vc, &op) >= 0) {
  122. memcpy(&vo, &test, sizeof(test));
  123. ctx->hasAudio = 1;
  124. } else
  125. ogg_stream_clear(&test);
  126. }
  127. }
  128. /* neither audio nor video streams found */
  129. if(!ctx->hasVideo && !ctx->hasAudio) goto cleanup;
  130. /* we're expecting more header packets. */
  131. while(!ctx->stop && ctx->f && !feof(ctx->f) &&
  132. ((ctx->hasVideo && ctx->hasVideo < 3) || (ctx->hasAudio && ctx->hasAudio < 3))) {
  133. while(ctx->hasVideo && (ctx->hasVideo < 3)) {
  134. if(ogg_stream_packetout(&to, &op) != 1 || ctx->stop) break;
  135. if(!th_decode_headerin(&ti, &tc, &ts, &op)) { ctx->hasVideo = 0; break; }
  136. ctx->hasVideo++;
  137. }
  138. while(ctx->hasAudio && (ctx->hasAudio < 3)) {
  139. if(ogg_stream_packetout(&vo, &op) != 1 || ctx->stop) break;
  140. if(vorbis_synthesis_headerin(&vi, &vc, &op)) { ctx->hasAudio = 0; break; }
  141. ctx->hasAudio++;
  142. }
  143. if(ogg_sync_pageout(&oy, &og) > 0) {
  144. if(ctx->hasVideo) ogg_stream_pagein(&to, &og);
  145. if(ctx->hasAudio) ogg_stream_pagein(&vo, &og);
  146. } else
  147. theora_getdata(ctx, &oy);
  148. }
  149. /* and now we have it all. initialize decoders */
  150. if(ctx->hasVideo && ti.pixel_fmt == TH_PF_420 && ti.pic_width > 0 && ti.pic_height > 0 &&
  151. ti.pic_width < 16384 && ti.pic_height < 16384 && (td = th_decode_alloc(&ti, ts))) {
  152. ret = 0; th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &ret, sizeof(ret)); /* turn off post processing */
  153. ctx->w = ti.pic_width;
  154. ctx->h = ti.pic_height;
  155. } else {
  156. /* tear down the partial theora setup */
  157. th_info_clear(&ti);
  158. th_comment_clear(&tc);
  159. ctx->hasVideo = ctx->w = ctx->h = 0;
  160. }
  161. if(ts) { th_setup_free(ts); ts = NULL; }
  162. if(ctx->hasAudio){
  163. vorbis_synthesis_init(&vd, &vi);
  164. if(vi.channels > 2) {
  165. /* sorry, 5.1 not supported (yet), needs SDL_ConvertAudio, see below */
  166. vorbis_info_clear(&vi);
  167. ctx->hasAudio = 0;
  168. } else
  169. vorbis_block_init(&vd, &vb);
  170. } else {
  171. vorbis_info_clear(&vi);
  172. vorbis_comment_clear(&vc);
  173. }
  174. /********************** that was only the setup so far... now do the real thing **********************/
  175. ctx->started = 1;
  176. while(!ctx->stop && (ctx->hasVideo || ctx->hasAudio)) {
  177. /* read in data */
  178. if(doread) {
  179. doread = 0;
  180. if(theora_getdata(ctx, &oy) > 0)
  181. while(!ctx->stop && ogg_sync_pageout(&oy, &og) > 0) {
  182. if(ctx->hasVideo) ogg_stream_pagein(&to, &og);
  183. if(ctx->hasAudio) ogg_stream_pagein(&vo, &og);
  184. }
  185. else
  186. break;
  187. }
  188. /*** parse audio ***/
  189. while(!ctx->stop && ctx->hasAudio && (ctx->ahead + 1) % THEORA_QUEUE_SIZE != ctx->atail) {
  190. /* if there's pending, decoded audio, grab it */
  191. if((ret = vorbis_synthesis_pcmout(&vd, &raw)) > 0) {
  192. vorbis_synthesis_read(&vd, ret);
  193. ctx->chunk[ctx->ahead].volume = MIX_MAX_VOLUME;
  194. ctx->chunk[ctx->ahead].allocated = 0;
  195. ctx->chunk[ctx->ahead].alen = i = sizeof(int16_t) * (vi.channels < 2 ? 2 : vi.channels) * ret;
  196. ctx->chunk[ctx->ahead].abuf = (Uint8*)realloc(ctx->chunk[ctx->ahead].abuf, i);
  197. /* deinterlaced float -> interlaced short int pcm */
  198. pcm = (int16_t*)ctx->chunk[ctx->ahead].abuf;
  199. if(pcm) {
  200. for(i = 0; i < ret; i++)
  201. /* do mono -> stereo the simple and fast way */
  202. if(vi.channels == 1) {
  203. val = raw[0][i]; ival = (int16_t)(val < -1.0f ? -32768 : (val > 1.0f ? 32767 : val * 32767.0f));
  204. *(pcm++) = ival;
  205. *(pcm++) = ival;
  206. } else
  207. for(j = 0; j < (uint32_t)vi.channels; j++) {
  208. val = raw[j][i];
  209. *(pcm++) = (int16_t)(val < -1.0f ? -32768 : (val > 1.0f ? 32767 : val * 32767.0f));
  210. }
  211. /* if there are more than stereo channels or frequency doesn't match we must convert */
  212. if(vi.rate != 44100 || vi.channels > 2) {
  213. /* TODO: set up SDL_ConvertAudio. Should solve 5.1 issue too */
  214. }
  215. /* add to queue */
  216. ctx->ahead = (ctx->ahead + 1) % THEORA_QUEUE_SIZE;
  217. ctx->started |= 2;
  218. }
  219. } else {
  220. if(ogg_stream_packetout(&vo, &op) > 0) {
  221. if(vorbis_synthesis(&vb, &op) == 0) vorbis_synthesis_blockin(&vd, &vb);
  222. } else {
  223. doread = 1;
  224. break;
  225. }
  226. }
  227. }
  228. /*** parse video ***/
  229. while(!ctx->stop && ctx->hasVideo && (ctx->vhead + 1) % THEORA_QUEUE_SIZE != ctx->vtail) {
  230. /* theora is one in, one out... */
  231. if(ogg_stream_packetout(&to, &op) > 0) {
  232. if(op.granulepos >= 0)
  233. th_decode_ctl(td, TH_DECCTL_SET_GRANPOS, &op.granulepos, sizeof(op.granulepos));
  234. videobuf_granulepos = 0;
  235. if(th_decode_packetin(td, &op, &videobuf_granulepos) == 0) {
  236. if(th_decode_ycbcr_out(td, ycbcr) == 0) {
  237. ctx->frame[ctx->vhead].playms = (uint32_t)(th_granule_time(td, videobuf_granulepos) * 1000.0);
  238. if(!ctx->frame[ctx->vhead].vbuf)
  239. ctx->frame[ctx->vhead].vbuf = (uint8_t*)malloc(ti.pic_width * ti.pic_height * 2);
  240. dst = ctx->frame[ctx->vhead].vbuf;
  241. if(dst) {
  242. /* frame420 to YUVPlanar */
  243. p = ycbcr[0].data + (ti.pic_x & ~1) + ycbcr[0].stride * (ti.pic_y & ~1);
  244. uvoff = (ti.pic_x / 2) + (ycbcr[1].stride) * (ti.pic_y / 2);
  245. w = ti.pic_width / 2; h = ti.pic_height / 2;
  246. for(j = 0; j < ti.pic_height; j++, dst += ti.pic_width, p += ycbcr[0].stride)
  247. memcpy(dst, p, ti.pic_width);
  248. for(j = 0, p = ycbcr[1].data + uvoff; j < h; j++, dst += w, p += ycbcr[1].stride) memcpy(dst, p, w);
  249. for(j = 0, p = ycbcr[2].data + uvoff; j < h; j++, dst += w, p += ycbcr[2].stride) memcpy(dst, p, w);
  250. /* add to queue */
  251. ctx->vhead = (ctx->vhead + 1) % THEORA_QUEUE_SIZE;
  252. ctx->started |= 4;
  253. }
  254. }
  255. }
  256. } else {
  257. doread = 1;
  258. break;
  259. }
  260. }
  261. }
  262. /**************************************** clean up *****************************************/
  263. cleanup:
  264. if(ts) { th_setup_free(ts); ts = NULL; }
  265. if(ctx->hasAudio) {
  266. ogg_stream_clear(&vo);
  267. vorbis_block_clear(&vb);
  268. vorbis_dsp_clear(&vd);
  269. }
  270. vorbis_comment_clear(&vc);
  271. vorbis_info_clear(&vi);
  272. if(ctx->hasVideo) ogg_stream_clear(&to);
  273. if(td) { th_decode_free(td); td = NULL; }
  274. th_comment_clear(&tc);
  275. th_info_clear(&ti);
  276. ogg_sync_clear(&oy);
  277. ctx->started = ctx->done = 1;
  278. return 0;
  279. }
  280. /**
  281. * Start decoder
  282. */
  283. void theora_start(theora_t *ctx, FILE *f)
  284. {
  285. if(!ctx || !f) return;
  286. ctx->f = f;
  287. ctx->stop = ctx->done = ctx->started = ctx->hasVideo = ctx->hasAudio = 0;
  288. ctx->th = SDL_CreateThread(theora_producer, "producer", ctx);
  289. /* wait until the producer thread detects the streams and sets hasAudio and hasVideo flags */
  290. while(!ctx->started) SDL_Delay(10);
  291. /* wait until there's some data in the circular buffers to consume */
  292. while(!ctx->done && ctx->started != (1 | (ctx->hasAudio ? 2 : 0) | (ctx->hasVideo ? 4 : 0))) SDL_Delay(10);
  293. ctx->baseticks = SDL_GetTicks();
  294. }
  295. /**
  296. * Stop decoder
  297. */
  298. void theora_stop(theora_t *ctx)
  299. {
  300. int i;
  301. if(!ctx) return;
  302. ctx->stop = 1;
  303. if(ctx->th) { SDL_WaitThread(ctx->th, &i); ctx->th = NULL; }
  304. /* failsafe */
  305. while(!ctx->done) SDL_Delay(10);
  306. /* free internal buffers */
  307. for(i = 0; i < THEORA_QUEUE_SIZE; i++) {
  308. if(ctx->chunk[i].abuf) { free(ctx->chunk[i].abuf); ctx->chunk[i].abuf = NULL; }
  309. if(ctx->frame[i].vbuf) { free(ctx->frame[i].vbuf); ctx->frame[i].vbuf = NULL; }
  310. }
  311. ctx->stop = ctx->done = ctx->started = ctx->ahead = ctx->atail = ctx->vhead = ctx->vtail = ctx->hasAudio = ctx->hasVideo = 0;
  312. }
  313. /**
  314. * Returns false if playing is finished (that is, decoding is done and everything is consumed from the buffers)
  315. */
  316. int theora_playing(theora_t *ctx)
  317. {
  318. return ctx && !ctx->stop && (!ctx->done || ctx->ahead != ctx->atail || ctx->vhead != ctx->vtail);
  319. }
  320. /**
  321. * Consume data from audio buffer
  322. */
  323. Mix_Chunk *theora_audio(theora_t *ctx)
  324. {
  325. volatile Mix_Chunk *ret;
  326. if(!ctx || !ctx->hasAudio || ctx->stop) return NULL;
  327. /* this is bad. we may have consumed the audio faster than the producer could produce. Wait for a bit. */
  328. while(!ctx->done && ctx->atail == ctx->ahead);
  329. /* if the queue is still empty, that means end of audio */
  330. if(ctx->atail == ctx->ahead) return NULL;
  331. ret = &ctx->chunk[ctx->atail];
  332. ctx->atail = (ctx->atail + 1) % THEORA_QUEUE_SIZE;
  333. return (Mix_Chunk*)ret;
  334. }
  335. /**
  336. * Consume data from video buffer and update a texture with it
  337. */
  338. void theora_video(theora_t *ctx, SDL_Texture *texture)
  339. {
  340. int i, pitch = 0;
  341. Uint8 *y, *u, *v, *dst = NULL;
  342. Uint32 now;
  343. /* here it is not a problem if the queue is temporarily empty, since we're not running inside SDL mixer callbacks */
  344. if(!ctx || !texture || !ctx->hasVideo || ctx->stop || ctx->vtail == ctx->vhead) return;
  345. now = SDL_GetTicks() - ctx->baseticks;
  346. if(ctx->frame[ctx->vtail].playms > now) return;
  347. /* handle frame drop, when the next frame is in the past too */
  348. while((ctx->vtail + 1) % THEORA_QUEUE_SIZE != ctx->vhead && ctx->frame[(ctx->vtail + 1) % THEORA_QUEUE_SIZE].vbuf &&
  349. ctx->frame[(ctx->vtail + 1) % THEORA_QUEUE_SIZE].playms < now) ctx->vtail = (ctx->vtail + 1) % THEORA_QUEUE_SIZE;
  350. if(ctx->vtail == ctx->vhead || !ctx->frame[ctx->vtail].vbuf) return;
  351. /* update the texture with the current frame */
  352. i = ctx->vtail; ctx->vtail = (ctx->vtail + 1) % THEORA_QUEUE_SIZE;
  353. SDL_LockTexture(texture, NULL, (void**)&dst, &pitch);
  354. /* failsafe if SDL failed to lock the texture */
  355. if(dst && pitch > 0) {
  356. y = ctx->frame[i].vbuf;
  357. u = y + (ctx->w * ctx->h);
  358. v = u + ((ctx->w / 2) * (ctx->h / 2));
  359. for (i = 0; i < ctx->h; i++, y += ctx->w, dst += pitch) memcpy(dst, y, ctx->w);
  360. for (i = 0; i < ctx->h / 2; i++, u += ctx->w / 2, dst += pitch / 2) memcpy(dst, u, ctx->w / 2);
  361. for (i = 0; i < ctx->h / 2; i++, v += ctx->w / 2, dst += pitch / 2) memcpy(dst, v, ctx->w / 2);
  362. }
  363. SDL_UnlockTexture(texture);
  364. }
  365. /**
  366. * Get the duration of an ogg file in millisec
  367. */
  368. # define TH_VERSION_CHECK(_info,_maj,_min,_sub) \
  369. ((_info)->version_major>(_maj)||((_info)->version_major==(_maj)&& \
  370. (((_info)->version_minor>(_min)||((_info)->version_minor==(_min)&& \
  371. (_info)->version_subminor>=(_sub))))))
  372. uint64_t theora_getduration(FILE *f)
  373. {
  374. uint64_t size, dur = 0;
  375. uint8_t *buff;
  376. char *buffer;
  377. int hv, ha, s, sv = 0, sa = 0;
  378. ogg_int64_t granulepos, iframe, pframe;
  379. ogg_packet op;
  380. ogg_sync_state oy;
  381. ogg_page og;
  382. ogg_stream_state vo;
  383. ogg_stream_state to;
  384. ogg_stream_state test;
  385. th_info ti;
  386. th_comment tc;
  387. th_setup_info *ts;
  388. vorbis_info vi;
  389. vorbis_dsp_state vd;
  390. vorbis_comment vc;
  391. if(!f) return 0;
  392. fseek(f, 0, SEEK_END);
  393. size = (uint64_t)ftell(f);
  394. fseek(f, 0, SEEK_SET);
  395. ogg_sync_init(&oy);
  396. vorbis_info_init(&vi);
  397. vorbis_comment_init(&vc);
  398. th_comment_init(&tc);
  399. th_info_init(&ti);
  400. ts = NULL;
  401. /* Ogg file open; parse the headers */
  402. /* Only interested in Vorbis/Theora streams */
  403. s = hv = ha = 0;
  404. while (!s) {
  405. buffer = ogg_sync_buffer(&oy, 4096);
  406. s = fread(buffer, 1, 4096, f);
  407. if(s <= 0 || ogg_sync_wrote(&oy, s)) break;
  408. s = 0;
  409. while(ogg_sync_pageout(&oy, &og) > 0) {
  410. if(!ogg_page_bos(&og)) {
  411. if(hv) ogg_stream_pagein(&to, &og);
  412. if(ha) ogg_stream_pagein(&vo, &og);
  413. s = 1;
  414. break;
  415. }
  416. ogg_stream_init(&test, ogg_page_serialno(&og));
  417. ogg_stream_pagein(&test, &og);
  418. ogg_stream_packetout(&test, &op);
  419. if(!hv && th_decode_headerin(&ti, &tc, &ts, &op) >= 0) {
  420. memcpy(&to, &test, sizeof(test));
  421. sv = ogg_page_serialno(&og);
  422. hv = 1;
  423. } else
  424. if(!ha && vorbis_synthesis_headerin(&vi, &vc, &op) >= 0) {
  425. memcpy(&vo, &test, sizeof(test));
  426. sa = ogg_page_serialno(&og);
  427. ha = 1;
  428. } else
  429. ogg_stream_clear(&test);
  430. }
  431. }
  432. if(ha || hv) {
  433. /* we're expecting more header packets. */
  434. while(!feof(f) && ((hv && hv < 3) || (ha && ha < 3))) {
  435. while(hv && (hv < 3)) {
  436. if(ogg_stream_packetout(&to, &op) != 1) break;
  437. if(!th_decode_headerin(&ti, &tc, &ts, &op)) { hv = 0; break; }
  438. hv++;
  439. }
  440. while(ha && (ha < 3)) {
  441. if(ogg_stream_packetout(&vo, &op) != 1) break;
  442. if(vorbis_synthesis_headerin(&vi, &vc, &op)) { ha = 0; break; }
  443. ha++;
  444. }
  445. if(ogg_sync_pageout(&oy, &og) > 0) {
  446. if(hv) ogg_stream_pagein(&to, &og);
  447. if(ha) ogg_stream_pagein(&vo, &og);
  448. } else {
  449. buffer = ogg_sync_buffer(&oy, 4096);
  450. s = fread(buffer, 1, 4096, f);
  451. if(s <= 0 || ogg_sync_wrote(&oy, s)) break;
  452. }
  453. }
  454. /* and now we have it all. initialize ti and vi structures */
  455. if(!(hv && ti.pixel_fmt == TH_PF_420 && ti.pic_width > 0 && ti.pic_height > 0 &&
  456. ti.pic_width < 16384 && ti.pic_height < 16384)) {
  457. /* tear down the partial theora setup */
  458. th_info_clear(&ti);
  459. th_comment_clear(&tc);
  460. hv = 0;
  461. }
  462. if(ts) th_setup_free(ts);
  463. if(ha){
  464. vorbis_synthesis_init(&vd, &vi);
  465. } else {
  466. vorbis_info_clear(&vi);
  467. vorbis_comment_clear(&vc);
  468. }
  469. /* read in the last 128K of the file. Forget tht horrible ogg_sync API from now on */
  470. if(size > 128 * 1024) {
  471. size = 128 * 1024;
  472. fseek(f, -128 * 1024, SEEK_END);
  473. } else {
  474. fseek(f, 0, SEEK_SET);
  475. }
  476. buff = (uint8_t*)malloc(size);
  477. if(buff) {
  478. fread(buff, size, 1, f);
  479. /* locate the last ogg packet */
  480. s = (hv ? sv : sa);
  481. for(og.header = buff + size - 19; og.header > buff && (memcmp(og.header, "OggS", 4) || ogg_page_serialno(&og) != s);
  482. og.header--);
  483. if(og.header > buff) {
  484. granulepos = ogg_page_granulepos(&og);
  485. /* if we have found the last packet of the audio stream */
  486. if(s == sa) {
  487. dur = (uint64_t)((granulepos * 1000 + vi.rate - 1) / vi.rate);
  488. } else
  489. /* if we have found the last packet of the video stream */
  490. if(s == sv) {
  491. /* this is from th_granule_time() */
  492. iframe = granulepos >> ti.keyframe_granule_shift;
  493. pframe = granulepos - (iframe << ti.keyframe_granule_shift);
  494. granulepos = iframe + pframe - TH_VERSION_CHECK(&ti,3,2,1);
  495. if(ti.fps_numerator < 1) ti.fps_numerator = 1;
  496. dur = (uint64_t)(((granulepos + 1) * 1000 * ti.fps_denominator + ti.fps_numerator - 1) / ti.fps_numerator);
  497. }
  498. }
  499. free(buff);
  500. }
  501. }
  502. fseek(f, 0, SEEK_SET);
  503. if(ha) {
  504. ogg_stream_clear(&vo);
  505. vorbis_dsp_clear(&vd);
  506. }
  507. vorbis_comment_clear(&vc);
  508. vorbis_info_clear(&vi);
  509. if(hv) ogg_stream_clear(&to);
  510. th_comment_clear(&tc);
  511. th_info_clear(&ti);
  512. ogg_sync_clear(&oy);
  513. return dur;
  514. }