Video.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /******************************************************************************
  2. DX9 requires deletion of IMAGE_RT on Display.lost
  3. /******************************************************************************/
  4. #include "stdafx.h"
  5. #include "../../../ThirdPartyLibs/begin.h"
  6. #if SUPPORT_THEORA
  7. #include "../../../ThirdPartyLibs/Theora/include/theora/theora.h"
  8. #endif
  9. #if SUPPORT_VORBIS || SUPPORT_OPUS || SUPPORT_VP // may be used by Vorbis/Opus/VP inside WEBM
  10. #pragma warning(push)
  11. #pragma warning(disable:4996) // strcpy
  12. #include "../../../ThirdPartyLibs/VP/libvpx/third_party/libwebm/mkvparser/mkvparser.h"
  13. #include "../../../ThirdPartyLibs/VP/libvpx/third_party/libwebm/mkvparser/mkvparser.cc"
  14. #pragma warning(pop)
  15. #endif
  16. #if SUPPORT_VP
  17. #include "../../../ThirdPartyLibs/VP/libvpx/vpx/vpx_decoder.h"
  18. #include "../../../ThirdPartyLibs/VP/libvpx/vpx/vp8dx.h"
  19. #include "../../../ThirdPartyLibs/VP/libvpx/webmdec.h"
  20. #include "../../../ThirdPartyLibs/VP/libvpx/webmdec.cc" // always include to avoid having to do separate WindowsUniversal libs for 32/64
  21. #undef UNUSED
  22. #endif
  23. #include "../../../ThirdPartyLibs/end.h"
  24. /******************************************************************************/
  25. namespace EE{
  26. /******************************************************************************/
  27. #if DX9
  28. Memx<Image> VideoTextures;
  29. #endif
  30. /******************************************************************************/
  31. #if SUPPORT_THEORA
  32. struct Theora : VideoCodec
  33. {
  34. Int p;
  35. ogg_sync_state oy;
  36. ogg_page og;
  37. ogg_stream_state vo;
  38. ogg_stream_state to;
  39. ogg_packet op;
  40. theora_info ti;
  41. theora_comment tc;
  42. theora_state td;
  43. void zero()
  44. {
  45. p=0;
  46. Zero(oy);
  47. Zero(og);
  48. Zero(vo);
  49. Zero(to);
  50. Zero(op);
  51. Zero(ti);
  52. Zero(tc);
  53. Zero(td);
  54. }
  55. void del()
  56. {
  57. if(p)
  58. {
  59. ogg_stream_clear (&to);
  60. theora_clear (&td);
  61. theora_comment_clear(&tc);
  62. theora_info_clear (&ti);
  63. }
  64. ogg_sync_clear(&oy);
  65. zero();
  66. }
  67. virtual Bool create(Video &video)
  68. {
  69. del();
  70. ogg_sync_init(&oy);
  71. theora_comment_init(&tc);
  72. theora_info_init (&ti);
  73. for(;;)
  74. {
  75. if(!buffer_data(video))break;
  76. Bool read=false;
  77. while(ogg_sync_pageout(&oy, &og)>0)
  78. {
  79. ogg_stream_state test;
  80. if(!ogg_page_bos(&og))
  81. {
  82. queue_page();
  83. goto done;
  84. }
  85. ogg_stream_init (&test, ogg_page_serialno(&og));
  86. ogg_stream_pagein (&test, &og);
  87. ogg_stream_packetout(&test, &op);
  88. if(!p && theora_decode_header(&ti, &tc, &op)>=0)
  89. {
  90. CopyFast(&to, &test, SIZE(test));
  91. p=1;
  92. }else
  93. {
  94. ogg_stream_clear(&test);
  95. }
  96. read=true;
  97. }
  98. if(!read)break;
  99. }
  100. done:;
  101. while(p && p<3)
  102. {
  103. int ret;
  104. while(p && p<3 && (ret=ogg_stream_packetout(&to, &op)))
  105. {
  106. if(ret<0 || theora_decode_header(&ti, &tc, &op))return false; // error parsing Theora stream headers; corrupt stream?
  107. if(++p==3)break;
  108. }
  109. if(ogg_sync_pageout(&oy, &og)>0)queue_page();else
  110. if(!buffer_data(video))return false; // end of file while searching for codec headers
  111. }
  112. if(p)
  113. {
  114. theora_decode_init(&td, &ti);
  115. }else
  116. {
  117. theora_info_clear (&ti);
  118. theora_comment_clear(&tc);
  119. }
  120. while(ogg_sync_pageout(&oy, &og)>0)queue_page();
  121. return p!=0;
  122. }
  123. void queue_page() {if(p)ogg_stream_pagein(&to, &og);}
  124. Int buffer_data(Video &video)
  125. {
  126. int bytes=video._file.getReturnSize(ogg_sync_buffer(&oy, 4096), 4096);
  127. ogg_sync_wrote(&oy, bytes);
  128. return bytes;
  129. }
  130. virtual RESULT nextFrame(Video &video, Flt &time)
  131. {
  132. for(;;)
  133. {
  134. for(; p; )
  135. {
  136. if(ogg_stream_packetout(&to, &op)<=0)break;
  137. theora_decode_packetin (&td, &op);
  138. time=theora_granule_time(&td, td.granulepos);
  139. return OK;
  140. }
  141. if(video._file.end())return END;
  142. for(buffer_data(video); ogg_sync_pageout(&oy, &og)>0; )queue_page();
  143. }
  144. }
  145. virtual void frameToImage(Video &video)
  146. {
  147. yuv_buffer yuv; theora_decode_YUVout(&td, &yuv);
  148. if(yuv.y_width>0 && yuv.y_height>0)
  149. {
  150. Int offset_x=ti.offset_x , offset_x_uv=(offset_x*yuv.uv_width )/yuv.y_width ,
  151. offset_y=ti.offset_y , offset_y_uv=(offset_y*yuv.uv_height)/yuv.y_height,
  152. width =ti.frame_width , width_uv=( width *yuv.uv_width )/yuv.y_width ,
  153. height =ti.frame_height, height_uv=(height *yuv.uv_height)/yuv.y_height;
  154. yuv.y+=offset_x + offset_y *yuv. y_stride;
  155. yuv.u+=offset_x_uv + offset_y_uv*yuv.uv_stride;
  156. yuv.v+=offset_x_uv + offset_y_uv*yuv.uv_stride;
  157. video.frameToImage(width, height, width_uv, height_uv, yuv.y, yuv.u, yuv.v, yuv.y_stride, yuv.uv_stride, yuv.uv_stride);
  158. }
  159. }
  160. ~Theora() {del ();}
  161. Theora() {zero();}
  162. };
  163. #endif
  164. /******************************************************************************/
  165. #if SUPPORT_VP
  166. struct MkvReader : mkvparser::IMkvReader
  167. {
  168. File &f;
  169. MkvReader(File &f) : f(f) {}
  170. virtual int Read(long long pos, long len, unsigned char* buf) // 0=OK, -1=error
  171. {
  172. if(len==0)return 0;
  173. if(!f.pos(pos))return -1;
  174. return f.getFast(buf, len) ? 0 : -1;
  175. }
  176. virtual int Length(long long* total, long long* available) // 0=OK, -1=error
  177. {
  178. if(total )*total =f.size();
  179. if(available)*available=f.size();
  180. return 0;
  181. }
  182. };
  183. struct VP : VideoCodec
  184. {
  185. Bool valid;
  186. uint8_t *buf;
  187. size_t buffer_size;
  188. VpxInputContext input;
  189. WebmInputContext webm;
  190. vpx_codec_ctx_t decoder;
  191. void zero()
  192. {
  193. valid=false; buf=null; buffer_size=0;
  194. Zero(input); Zero(webm); Zero(decoder);
  195. }
  196. void del()
  197. {
  198. vpx_codec_destroy(&decoder);
  199. webm_free(&webm);
  200. DeleteN(buf);
  201. zero();
  202. }
  203. virtual Bool create(Video &video)
  204. {
  205. del();
  206. webm.reader=new MkvReader(video._file);
  207. if(file_is_webm(&webm, &input))
  208. {
  209. input.file_type=FILE_TYPE_WEBM;
  210. if(input.fourcc==CC4('V', 'P', '9', '0'))
  211. if(!webm_guess_framerate(&webm, &input))
  212. {
  213. Flt fps=Flt(input.framerate.numerator)/input.framerate.denominator;
  214. vpx_codec_dec_cfg_t cfg; Zero(cfg);
  215. cfg.threads=Min(2, Cpu.threads());
  216. if(!vpx_codec_dec_init(&decoder, vpx_codec_vp9_dx(), &cfg, 0))
  217. {
  218. valid=true;
  219. return true;
  220. }
  221. }
  222. }
  223. del(); return false;
  224. }
  225. virtual RESULT nextFrame(Video &video, Flt &time)
  226. {
  227. if(valid)
  228. {
  229. Int result=webm_read_frame(&webm, &buf, &buffer_size); // 0=ok, 1=end, -1=error
  230. if( result==1)return END;
  231. if(!result && !vpx_codec_decode(&decoder, buf, (unsigned int)buffer_size, null, 0))
  232. {
  233. time=webm.timestamp_ns/1000000000.0;
  234. return OK;
  235. }
  236. del();
  237. }
  238. return ERROR;
  239. }
  240. virtual void frameToImage(Video &video)
  241. {
  242. if(valid)
  243. {
  244. vpx_codec_iter_t iter=null;
  245. if(vpx_image_t *image=vpx_codec_get_frame(&decoder, &iter))
  246. {
  247. Int w=image->d_w, h=image->d_h;
  248. video.frameToImage(w, h, (w+1)/2, (h+1)/2, image->planes[0], image->planes[1], image->planes[2], image->stride[0], image->stride[1], image->stride[2]);
  249. }
  250. }
  251. }
  252. VP() {zero();}
  253. ~VP() {del ();}
  254. };
  255. #endif
  256. /******************************************************************************/
  257. void Video::zero()
  258. {
  259. _codec =VIDEO_NONE;
  260. _loop =false;
  261. _mode =DEFAULT;
  262. _w=_h=_br=0;
  263. _time =0;
  264. _time_past=0;
  265. _fps =0;
  266. _d =null;
  267. _tex_ptr =null;
  268. }
  269. Video::Video() {zero();}
  270. void Video::release()
  271. {
  272. Delete(_d);
  273. _file.del();
  274. }
  275. void Video::del()
  276. {
  277. release();
  278. _lum .del();
  279. _u .del();
  280. _v .del();
  281. _tex .del();
  282. #if DX9
  283. if(_tex_ptr){VideoTextures.removeData(_tex_ptr); _tex_ptr=null;}
  284. #endif
  285. zero();
  286. }
  287. Bool Video::create(C Str &name, Bool loop, MODE mode)
  288. {
  289. del();
  290. if(!name.is())return true;
  291. if(_file.readTry(name))
  292. {
  293. #if SUPPORT_VP
  294. VP vp;
  295. if(vp.create(T))
  296. {
  297. _codec=VIDEO_VP9;
  298. _w =vp.input.width;
  299. _h =vp.input.height;
  300. _br =0;
  301. _fps =Flt(vp.input.framerate.numerator)/vp.input.framerate.denominator;
  302. _d =new VP; Swap(*(VP*)_d, vp);
  303. }else
  304. #endif
  305. {
  306. #if SUPPORT_THEORA
  307. _file.pos(0); // reset file position after VP attempt
  308. Theora theora;
  309. if(theora.create(T))
  310. {
  311. _codec=VIDEO_THEORA;
  312. _w =theora.ti.frame_width;
  313. _h =theora.ti.frame_height;
  314. _br =((theora.ti.target_bitrate>0) ? theora.ti.target_bitrate : (theora.ti.keyframe_data_target_bitrate>0) ? theora.ti.keyframe_data_target_bitrate : 0);
  315. _fps =Flt(theora.ti.fps_numerator)/theora.ti.fps_denominator;
  316. _d =new Theora; Swap(*(Theora*)_d, theora);
  317. }
  318. #endif
  319. }
  320. if(_codec)
  321. {
  322. _loop=loop;
  323. _mode=mode;
  324. #if DX9
  325. if(_mode==IMAGE)_tex_ptr=&VideoTextures.New(); // needed only for IMAGE because only this mode uses IMAGE_RT
  326. #endif
  327. return true;
  328. }
  329. }
  330. del(); return false;
  331. }
  332. Bool Video::create(C UID &id, Bool loop, MODE mode) {return create(id.valid() ? _EncodeFileName(id) : null, loop, mode);}
  333. /******************************************************************************/
  334. CChar8* Video::codecName()C {return CodecName(codec());}
  335. /******************************************************************************/
  336. Bool Video::frameToImage(Int w, Int h, Int w2, Int h2, CPtr lum_data, CPtr u_data, CPtr v_data, Int lum_pitch, Int u_pitch, Int v_pitch)
  337. {
  338. #if DX9
  339. #define VIDEO_IMAGE_TYPE IMAGE_L8
  340. #else
  341. #define VIDEO_IMAGE_TYPE IMAGE_R8
  342. #endif
  343. if(_mode==ALPHA)
  344. {
  345. if(_lum.w()!=w || _lum.h()!=h)if(!_lum.create2DTry(w, h, VIDEO_IMAGE_TYPE, 1, false))return false;
  346. _lum.setFrom(lum_data, lum_pitch);
  347. }else
  348. {
  349. if(_lum.w()!=w || _lum.h()!=h ){if(!_lum.create2DTry(w , h , VIDEO_IMAGE_TYPE, 1, false))return false; if(!Sh.h_YUV)AtomicSet(Sh.h_YUV, Sh.get("YUV"));}
  350. if(_u .w()!=w2 || _u .h()!=h2) if(!_u .create2DTry(w2, h2, VIDEO_IMAGE_TYPE, 1, false))return false;
  351. if(_v .w()!=w2 || _v .h()!=h2) if(!_v .create2DTry(w2, h2, VIDEO_IMAGE_TYPE, 1, false))return false;
  352. _lum.setFrom(lum_data, lum_pitch);
  353. _u .setFrom( u_data, u_pitch);
  354. _v .setFrom( v_data, v_pitch);
  355. if(_mode==IMAGE) // if want to create a texture
  356. {
  357. #if DX9
  358. Image *tex= _tex_ptr;
  359. #else
  360. Image *tex=&_tex;
  361. #endif
  362. if(tex)
  363. {
  364. if(tex->w()!=w || tex->h()!=h)tex->create(w, h, 1, IMAGE_DEFAULT, IMAGE_RT, 1);
  365. SyncLocker locker(D._lock); // needed for drawing in case this is called outside of Draw
  366. Image *rt[Elms(Renderer._cur)], *rtz=Renderer._cur_ds; REPAO(rt)=Renderer._cur[i];
  367. Renderer.set(tex, null, false);
  368. ALPHA_MODE alpha=D.alpha(ALPHA_NONE);
  369. draw(D.rect()); // use specified rectangle without fitting via 'drawFs' or 'drawFit'
  370. D.alpha(alpha);
  371. Renderer.set(rt[0], rt[1], rt[2], rt[3], rtz, true);
  372. }
  373. }
  374. }
  375. return true;
  376. }
  377. void Video::frameToImage()
  378. {
  379. if(_d)_d->frameToImage(T);
  380. }
  381. Bool Video::nextFrame()
  382. {
  383. if(_d)
  384. {
  385. Flt time; switch(_d->nextFrame(T, time))
  386. {
  387. default : error: release(); break;
  388. case VideoCodec::OK : ok : _time=time+_time_past; return true;
  389. case VideoCodec::END: // finished playing
  390. {
  391. if(_loop)
  392. {
  393. // restart
  394. _file.pos(0);
  395. _time_past=_time;
  396. if(_d->create(T))if(_d->nextFrame(T, time)==VideoCodec::OK)goto ok; // reset and set the first frame
  397. }
  398. goto error;
  399. }break;
  400. }
  401. }
  402. return false;
  403. }
  404. /******************************************************************************/
  405. C Image& Video::image()C
  406. {
  407. #if DX9
  408. if(_tex_ptr)return *_tex_ptr;
  409. #endif
  410. return _tex;
  411. }
  412. /******************************************************************************/
  413. Bool Video::update(Flt time)
  414. {
  415. if(_d)
  416. {
  417. Bool added=false;
  418. for(; T.time()<=time; )if(nextFrame())added=true;else break; // use <= so update(0) will set first frame
  419. if(added)frameToImage();
  420. return added || _d; // if there was a frame added or if the video still exists
  421. }
  422. return false;
  423. }
  424. /******************************************************************************/
  425. void Video::drawAlphaFs (C Video &alpha, FIT_MODE fit)C {return drawAlpha(alpha, T.fit(D.rect(), fit));}
  426. void Video::drawAlphaFit(C Video &alpha, C Rect &rect)C {return drawAlpha(alpha, T.fit( rect ));}
  427. void Video::drawAlpha (C Video &alpha, C Rect &rect)C
  428. {
  429. if(_lum.is())
  430. {
  431. if(!Sh.h_YUVA)AtomicSet(Sh.h_YUVA, Sh.get("YUVA"));
  432. Sh .h_ImageCol[1]->set(_u); MaterialClear();
  433. Sh .h_ImageCol[2]->set(_v);
  434. Sh .h_ImageCol[3]->set(alpha._lum);
  435. VI .shader(Sh.h_YUVA);
  436. _lum.draw (rect);
  437. VI .clear(); // force clear to reset custom shader, in case 'draw' doesn't process drawing
  438. }
  439. }
  440. void Video::drawFs (FIT_MODE fit)C {return draw(T.fit(D.rect(), fit));}
  441. void Video::drawFit(C Rect &rect)C {return draw(T.fit( rect ));}
  442. void Video::draw (C Rect &rect)C
  443. {
  444. if(_lum.is())
  445. {
  446. Sh .h_ImageCol[1]->set(_u); MaterialClear();
  447. Sh .h_ImageCol[2]->set(_v);
  448. VI .shader(Sh.h_YUV);
  449. _lum.draw (rect);
  450. VI .clear(); // force clear to reset custom shader, in case 'draw' doesn't process drawing
  451. }
  452. }
  453. /******************************************************************************/
  454. CChar8* CodecName(VIDEO_CODEC codec)
  455. {
  456. switch(codec)
  457. {
  458. default : return null;
  459. case VIDEO_THEORA : return "Theora";
  460. case VIDEO_VP9 : return "VP9";
  461. }
  462. }
  463. /******************************************************************************/
  464. #if DX9
  465. void VideoTexturesLost()
  466. {
  467. REPA(VideoTextures)
  468. {
  469. Image &image=VideoTextures[i];
  470. image.copyTry(image, -1, -1, -1, -1, IMAGE_2D); // replace with 2D texture so it can be used to draw on the IMAGE_RT later
  471. }
  472. }
  473. void VideoTexturesReset()
  474. {
  475. REPA(VideoTextures)
  476. {
  477. Image &image=VideoTextures[i];
  478. Image temp; Swap(temp, image);
  479. if(image.createTry(temp.w(), temp.h(), 1, temp.type(), IMAGE_RT, 1))temp.copyHw(image, true);
  480. }
  481. }
  482. #endif
  483. /******************************************************************************/
  484. }
  485. /******************************************************************************/