videoEncoderTheora.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifdef TORQUE_OGGTHEORA
  23. #include "videoCapture.h"
  24. #include "core/stream/fileStream.h"
  25. #include "console/console.h"
  26. #include "gfx/bitmap/gBitmap.h"
  27. #include "gfx/bitmap/bitmapUtils.h"
  28. #include "platform/threads/thread.h"
  29. #include "platform/threads/threadSafeDeque.h"
  30. #include "platform/threads/semaphore.h"
  31. #include "theora/theoraenc.h"
  32. #include "vorbis/codec.h"
  33. #include "vorbis/vorbisenc.h"
  34. #include "platform/profiler.h"
  35. //#define THEORA_ENCODER_SINGLE_THREAD
  36. // These are the coefficient lookup tables, so we don't need to multiply
  37. dALIGN( static S32 sYRGB[ 256 ][ 4 ] );
  38. dALIGN( static S32 sURGB[ 256 ][ 4 ] );
  39. dALIGN( static S32 sVRGB[ 256 ][ 4 ] );
  40. /// Initialize the lookup tables used in the RGB-YUV transcoding
  41. static void initLookupTables()
  42. {
  43. static bool sGenerated = false;
  44. if( !sGenerated )
  45. {
  46. for( S32 i = 0; i < 256; ++ i )
  47. {
  48. //Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16
  49. //U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128
  50. //V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128
  51. // Y coefficients
  52. sYRGB[ i ][ 0 ] = 66 * i;
  53. sYRGB[ i ][ 1 ] = 129 * i;
  54. sYRGB[ i ][ 2 ] = 25 * i + 128 + (16 << 8);
  55. // U coefficients
  56. sURGB[ i ][ 0 ] = -38 * i;
  57. sURGB[ i ][ 1 ] = -74 * i;
  58. sURGB[ i ][ 2 ] = 112 * i + 128 + (128 << 8);
  59. // V coefficients
  60. sVRGB[ i ][ 0 ] = 112 * i;
  61. sVRGB[ i ][ 1 ] = -94 * i;
  62. sVRGB[ i ][ 2 ] = -18 * i + 128 + (128 << 8);
  63. }
  64. sGenerated = true;
  65. }
  66. }
  67. /// Theora video capture encoder
  68. class VideoEncoderTheora : public VideoEncoder, public Thread
  69. {
  70. U32 mCurrentFrame;
  71. ogg_stream_state to; // take physical pages, weld into a logical stream of packets
  72. th_enc_ctx *td; // Theora encoder context
  73. th_info ti; // Theora info structure
  74. th_comment tc; // Theora comment structure
  75. FileStream mFile; // Output file
  76. th_ycbcr_buffer mBuffer; // YCbCr buffer
  77. GBitmap* mLastFrame;
  78. ThreadSafeDeque< GBitmap* > mFrameBitmapList; // List with unprocessed frame bitmaps
  79. Semaphore mSemaphore; //Semaphore for preventing the encoder from being lagged behind the game
  80. bool mErrorStatus; //Status flag, true if OK, false if an error ocurred
  81. /// Sets our error status
  82. bool setStatus(bool status)
  83. {
  84. mErrorStatus = status;
  85. return mErrorStatus;
  86. }
  87. bool getStatus() { return mErrorStatus; }
  88. /// Encodes one frame
  89. void encodeFrame( GBitmap* bitmap, bool isLast=false )
  90. {
  91. PROFILE_SCOPE(Theora_encodeFrame);
  92. //Copy bitmap to YUV buffer
  93. copyBitmapToYUV420(bitmap);
  94. PROFILE_START(th_encode_ycbcr_in);
  95. //Submit frame for encoding
  96. if (th_encode_ycbcr_in(td, mBuffer))
  97. {
  98. Platform::outputDebugString("VideoEncoderTheora::encodeFrame() - The buffer size does not match the frame size the encoder was initialized with, or encoding has already completed. .");
  99. setStatus(false);
  100. PROFILE_END();
  101. return;
  102. }
  103. PROFILE_END();
  104. //Fetch the encoded packets
  105. ogg_packet packet;
  106. if (!th_encode_packetout(td, isLast, &packet))
  107. {
  108. Platform::outputDebugString("VideoEncoderTheora::encodeFrame() - Internal Theora library error.");
  109. setStatus(false);
  110. return;
  111. }
  112. //Insert packet into vorbis stream page
  113. ogg_stream_packetin(&to,&packet);
  114. //Increment
  115. mCurrentFrame++;
  116. //Is there a video page flushed?
  117. ogg_page videopage;
  118. while (ogg_stream_pageout(&to,&videopage))
  119. {
  120. //Write the video page to disk
  121. mFile.write(videopage.header_len, videopage.header);
  122. mFile.write(videopage.body_len, videopage.body);
  123. F64 videotime = th_granule_time(td,ogg_page_granulepos(&videopage));
  124. if (videotime > 0)
  125. {
  126. S32 hundredths=(int)(videotime*100-(long)videotime*100);
  127. S32 seconds=(long)videotime%60;
  128. Platform::outputDebugString("Encoding time %g %02i.%02i", videotime, seconds, hundredths);
  129. }
  130. }
  131. mSemaphore.release();
  132. }
  133. bool process(bool ending)
  134. {
  135. if (!getStatus())
  136. return false;
  137. //Try getting a bitmap for encoding
  138. GBitmap* bitmap = NULL;
  139. if (mFrameBitmapList.tryPopFront(bitmap))
  140. {
  141. encodeFrame(bitmap, false);
  142. }
  143. //Delete previous bitmap
  144. if (!ending && bitmap)
  145. {
  146. if (mLastFrame)
  147. pushProcessedBitmap(mLastFrame);
  148. mLastFrame = bitmap;
  149. }
  150. //If we're stopping encoding, but didn't have a frame, re-encode the last frame
  151. if (ending && !bitmap && mLastFrame)
  152. {
  153. encodeFrame(mLastFrame, true);
  154. pushProcessedBitmap(mLastFrame);
  155. mLastFrame = NULL;
  156. }
  157. // We'll live while we have a last frame
  158. return (mLastFrame != NULL);
  159. }
  160. public:
  161. VideoEncoderTheora() :
  162. mCurrentFrame(0), td(NULL), mLastFrame(NULL)
  163. {
  164. setStatus(false);
  165. }
  166. virtual void run( void* arg )
  167. {
  168. _setName( "TheoraEncoderThread" );
  169. while (!checkForStop())
  170. process(false);
  171. // Encode all pending frames and close the last one
  172. while (process(true));
  173. }
  174. /// Begins accepting frames for encoding
  175. bool begin()
  176. {
  177. mPath += ".ogv";
  178. mCurrentFrame = 0;
  179. //Try opening the file for writing
  180. if ( !mFile.open( mPath, Torque::FS::File::Write ) )
  181. {
  182. Platform::outputDebugString( "VideoEncoderTheora::begin() - Failed to open output file '%s'!", mPath.c_str() );
  183. return setStatus(false);
  184. }
  185. ogg_stream_init(&to, Platform::getRandom() * S32_MAX);
  186. ogg_packet op;
  187. th_info_init(&ti);
  188. ti.frame_width = mResolution.x;
  189. ti.frame_height = mResolution.y;
  190. ti.pic_width = mResolution.x;
  191. ti.pic_height = mResolution.y;
  192. ti.pic_x = 0;
  193. ti.pic_y = 0;
  194. ti.fps_numerator = (int)(mFramerate * 1000.0f);
  195. ti.fps_denominator = 1000;
  196. ti.aspect_numerator = 0;
  197. ti.aspect_denominator = 0;
  198. ti.colorspace = TH_CS_UNSPECIFIED;
  199. ti.target_bitrate = 0;
  200. ti.quality = 63;
  201. ti.pixel_fmt = TH_PF_420;
  202. td = th_encode_alloc(&ti);
  203. if (td == NULL)
  204. {
  205. Platform::outputDebugString("VideoEncoderTheora::begin() - Theora initialization error.");
  206. return setStatus(false);
  207. }
  208. th_info_clear(&ti);
  209. // This is needed for youtube compatibility
  210. S32 vp3_compatible = 1;
  211. th_encode_ctl(td, TH_ENCCTL_SET_VP3_COMPATIBLE, &vp3_compatible, sizeof(vp3_compatible));
  212. // Set the encoder to max speed
  213. S32 speed_max;
  214. S32 ret;
  215. ret = th_encode_ctl(td, TH_ENCCTL_GET_SPLEVEL_MAX, &speed_max, sizeof(speed_max));
  216. if(ret<0){
  217. Platform::outputDebugString("VideoEncoderTheora::begin() - could not determine maximum speed level.");
  218. speed_max = 0;
  219. }
  220. ret = th_encode_ctl(td, TH_ENCCTL_SET_SPLEVEL, &speed_max, sizeof(speed_max));
  221. // write the bitstream header packets with proper page interleave
  222. th_comment_init(&tc);
  223. // first packet will get its own page automatically
  224. if(th_encode_flushheader(td,&tc,&op) <= 0)
  225. {
  226. Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Theora library error.");
  227. return setStatus(false);
  228. }
  229. ogg_page og;
  230. ogg_stream_packetin(&to,&op);
  231. if(ogg_stream_pageout(&to,&og) != 1)
  232. {
  233. Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Ogg library error.");
  234. return setStatus(false);
  235. }
  236. mFile.write(og.header_len, og.header);
  237. mFile.write(og.body_len, og.body);
  238. // create the remaining theora headers
  239. while((ret = th_encode_flushheader(td,&tc,&op)) != 0)
  240. {
  241. if(ret < 0)
  242. {
  243. Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Theora library error.");
  244. return setStatus(false);
  245. }
  246. ogg_stream_packetin(&to,&op);
  247. }
  248. // Flush the rest of our headers. This ensures
  249. // the actual data in each stream will start
  250. // on a new page, as per spec.
  251. while((ret = ogg_stream_flush(&to,&og)) != 0)
  252. {
  253. if(ret < 0)
  254. {
  255. Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Ogg library error.");
  256. return setStatus(false);
  257. }
  258. mFile.write(og.header_len, og.header);
  259. mFile.write(og.body_len, og.body);
  260. }
  261. //Initialize the YUV buffer
  262. S32 decimation[] = {0,1,1};
  263. for (U32 i=0; i<3; i++)
  264. {
  265. mBuffer[i].width = mResolution.x >> decimation[i];
  266. mBuffer[i].height = mResolution.y >> decimation[i];
  267. mBuffer[i].stride = mBuffer[i].width * sizeof(U8);
  268. mBuffer[i].data = new U8[mBuffer[i].width*mBuffer[i].height*sizeof(U8)];
  269. }
  270. //Initialize the YUV coefficient lookup tables
  271. initLookupTables();
  272. setStatus(true);
  273. #ifndef THEORA_ENCODER_SINGLE_THREAD
  274. start();
  275. #endif
  276. return getStatus();
  277. }
  278. /// Pushes a new frame into the video stream
  279. bool pushFrame( GBitmap * bitmap )
  280. {
  281. // Push the bitmap into the frame list
  282. mFrameBitmapList.pushBack( bitmap );
  283. mSemaphore.acquire();
  284. #ifdef THEORA_ENCODER_SINGLE_THREAD
  285. process(false);
  286. #endif
  287. return getStatus();
  288. }
  289. /// Finishes the encoding and closes the video
  290. bool end()
  291. {
  292. //Let's wait the thread stop doing whatever it needs to do
  293. stop();
  294. join();
  295. #ifdef THEORA_ENCODER_SINGLE_THREAD
  296. while (process(true));
  297. #endif
  298. th_encode_free(td);
  299. ogg_stream_clear(&to);
  300. th_comment_clear(&tc);
  301. mFile.close();
  302. return true;
  303. }
  304. void setResolution( Point2I* resolution )
  305. {
  306. /* Theora has a divisible-by-sixteen restriction for the encoded frame size */
  307. /* scale the picture size up to the nearest /16 and calculate offsets */
  308. resolution->x = (resolution->x) + 15 & ~0xF;
  309. resolution->y = (resolution->y) + 15 & ~0xF;
  310. mResolution = *resolution;
  311. }
  312. /// Converts the bitmap to YUV420 and copies it into our internal buffer
  313. void copyBitmapToYUV420( GBitmap* bitmap )
  314. {
  315. PROFILE_SCOPE(copyBitmapToYUV420);
  316. // Convert luma
  317. const U8* rgb = bitmap->getBits();
  318. // Chroma planes are half width and half height
  319. U32 w = mResolution.x / 2;
  320. U32 h = mResolution.y / 2;
  321. // We'll update two luminance rows at once
  322. U8* yuv_y0 = mBuffer[0].data;
  323. U8* yuv_y1 = mBuffer[0].data + mBuffer[0].stride;
  324. // Get pointers to chroma planes
  325. U8* yuv_u = mBuffer[1].data;
  326. U8* yuv_v = mBuffer[2].data;
  327. // We'll also need to read two RGB rows at once
  328. U32 rgbStride = mResolution.x * bitmap->getBytesPerPixel();
  329. const U8* row0 = rgb;
  330. const U8* row1 = row0 + rgbStride;
  331. for(U32 y = 0; y < h; y++)
  332. {
  333. for(U32 x = 0; x < w; x++)
  334. {
  335. // Fetch two RGB samples from each RGB row (for downsampling the chroma)
  336. U8 r0 = *row0++;
  337. U8 g0 = *row0++;
  338. U8 b0 = *row0++;
  339. U8 r1 = *row0++;
  340. U8 g1 = *row0++;
  341. U8 b1 = *row0++;
  342. U8 r2 = *row1++;
  343. U8 g2 = *row1++;
  344. U8 b2 = *row1++;
  345. U8 r3 = *row1++;
  346. U8 g3 = *row1++;
  347. U8 b3 = *row1++;
  348. // Convert the four RGB samples into four luminance samples
  349. *yuv_y0 = ( (sYRGB[r0][0] + sYRGB[g0][1] + sYRGB[b0][2]) >> 8);
  350. yuv_y0++;
  351. *yuv_y0 = ( (sYRGB[r1][0] + sYRGB[g1][1] + sYRGB[b1][2]) >> 8);
  352. yuv_y0++;
  353. *yuv_y1 = ( (sYRGB[r2][0] + sYRGB[g2][1] + sYRGB[b2][2]) >> 8);
  354. yuv_y1++;
  355. *yuv_y1 = ( (sYRGB[r3][0] + sYRGB[g3][1] + sYRGB[b3][2]) >> 8);
  356. yuv_y1++;
  357. // Downsample the four RGB samples
  358. U8 r = (r0 + r1 + r2 + r3) >> 2;
  359. U8 g = (g0 + g1 + g2 + g3) >> 2;
  360. U8 b = (b0 + b1 + b2 + b3) >> 2;
  361. // Convert downsampled RGB into chroma
  362. *yuv_u = ( (sURGB[r][0] + sURGB[g][1] + sURGB[b][2]) >> 8);
  363. *yuv_v = ( (sVRGB[r][0] + sVRGB[g][1] + sVRGB[b][2]) >> 8);
  364. yuv_u++;
  365. yuv_v++;
  366. }
  367. //Next RGB rows
  368. row0 += rgbStride;
  369. row1 += rgbStride;
  370. //Next luminance rows
  371. yuv_y0 += mBuffer[0].stride;
  372. yuv_y1 += mBuffer[0].stride;
  373. }
  374. }
  375. };
  376. REGISTER_VIDEO_ENCODER(VideoEncoderTheora, THEORA)
  377. #endif