aviwriter.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * Copyright 2011-2025 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
  4. */
  5. #ifndef AVIWRITER_H_HEADER_GUARD
  6. #define AVIWRITER_H_HEADER_GUARD
  7. #include <bx/readerwriter.h>
  8. // Simple AVI writer. VideoLAN and VirtualDub can decode it.
  9. // Needs some bits to get jiggled to work with other players. But it's good
  10. // enough for an example.
  11. struct AviWriter
  12. {
  13. AviWriter(bx::FileWriterI* _writer)
  14. : m_writer(_writer)
  15. , m_frame(NULL)
  16. , m_frameSize(0)
  17. , m_numFrames(0)
  18. , m_width(0)
  19. , m_height(0)
  20. , m_yflip(false)
  21. {
  22. }
  23. bool open(const char* _filePath, uint32_t _width, uint32_t _height, uint32_t _fps, bool _yflip)
  24. {
  25. if (!bx::open(m_writer, _filePath) )
  26. {
  27. return false;
  28. }
  29. m_frameSize = _width * _height * 3;
  30. m_frame = new uint8_t[m_frameSize + 8];
  31. m_numFrames = 0;
  32. m_width = _width;
  33. m_height = _height;
  34. // Bgfx returns _yflip true for OpenGL since bottom left corner is 0, 0. In D3D top left corner
  35. // is 0, 0. DIB expect OpenGL style coordinates, so this is inverted logic for AVI writer.
  36. m_yflip = !_yflip;
  37. bx::Error err;
  38. bx::StaticMemoryBlockWriter mem(m_frame, 8);
  39. // Stream Data (LIST 'movi' Chunk) http://msdn.microsoft.com/en-us/library/ms899496.aspx
  40. bx::write(&mem, BX_MAKEFOURCC('0', '0', 'd', 'b'), &err);
  41. bx::write(&mem, m_frameSize, &err);
  42. bx::write(m_writer, BX_MAKEFOURCC('R', 'I', 'F', 'F'), &err);
  43. m_riffSizeOffset = m_writer->seek();
  44. bx::write(m_writer, uint32_t(0), &err);
  45. bx::write(m_writer, BX_MAKEFOURCC('A', 'V', 'I', ' '), &err);
  46. // AVI RIFF Form http://msdn.microsoft.com/en-us/library/ms899422.aspx
  47. bx::write(m_writer, BX_MAKEFOURCC('L', 'I', 'S', 'T'), &err);
  48. bx::write(m_writer, uint32_t(192), &err);
  49. bx::write(m_writer, BX_MAKEFOURCC('h', 'd', 'r', 'l'), &err);
  50. // AVI Main Header http://msdn.microsoft.com/en-us/library/ms779632.aspx
  51. bx::write(m_writer, BX_MAKEFOURCC('a', 'v', 'i', 'h'), &err);
  52. bx::write(m_writer, uint32_t(56), &err);
  53. bx::write(m_writer, uint32_t(0), &err); // dwMicroSecPerFrame
  54. bx::write(m_writer, uint32_t(0), &err); // dwMaxBytesPerSec
  55. bx::write(m_writer, uint32_t(0), &err); // dwPaddingGranularity
  56. bx::write(m_writer, uint32_t(0x110), &err); // dwFlags
  57. m_totalFramesOffset = m_writer->seek();
  58. bx::write(m_writer, uint32_t(0), &err); // dwTotalFrames
  59. bx::write(m_writer, uint32_t(0), &err); // dwInitialFrames
  60. bx::write(m_writer, uint32_t(1), &err); // dwStreams
  61. bx::write(m_writer, uint32_t(0), &err); // dwSuggestedBufferSize
  62. bx::write(m_writer, _width, &err); // dwWidth
  63. bx::write(m_writer, _height, &err); // dwHeight
  64. bx::write(m_writer, uint32_t(0), &err); // dwReserved0
  65. bx::write(m_writer, uint32_t(0), &err); // dwReserved1
  66. bx::write(m_writer, uint32_t(0), &err); // dwReserved2
  67. bx::write(m_writer, uint32_t(0), &err); // dwReserved3
  68. bx::write(m_writer, BX_MAKEFOURCC('L', 'I', 'S', 'T'), &err);
  69. bx::write(m_writer, uint32_t(116), &err);
  70. bx::write(m_writer, BX_MAKEFOURCC('s', 't', 'r', 'l'), &err);
  71. // AVISTREAMHEADER Structure http://msdn.microsoft.com/en-us/library/ms779638.aspx
  72. bx::write(m_writer, BX_MAKEFOURCC('s', 't', 'r', 'h'), &err);
  73. bx::write(m_writer, uint32_t(56), &err);
  74. // AVI Stream Headers http://msdn.microsoft.com/en-us/library/ms899423.aspx
  75. bx::write(m_writer, BX_MAKEFOURCC('v', 'i', 'd', 's'), &err); // fccType
  76. bx::write(m_writer, BX_MAKEFOURCC('D', 'I', 'B', ' '), &err); // fccHandler
  77. bx::write(m_writer, uint32_t(0), &err); // dwFlags
  78. bx::write(m_writer, uint16_t(0), &err); // wPriority
  79. bx::write(m_writer, uint16_t(0), &err); // wLanguage
  80. bx::write(m_writer, uint32_t(0), &err); // dwInitialFrames
  81. bx::write(m_writer, uint32_t(1), &err); // dwScale
  82. bx::write(m_writer, _fps, &err); // dwRate
  83. bx::write(m_writer, uint32_t(0), &err); // dwStart
  84. m_lengthOffset = m_writer->seek();
  85. bx::write(m_writer, uint32_t(0), &err); // dwLength
  86. bx::write(m_writer, m_frameSize, &err); // dwSuggestedBufferSize
  87. bx::write(m_writer, UINT32_MAX, &err); // dwQuality
  88. bx::write(m_writer, uint32_t(0), &err); // dwSampleSize
  89. bx::write(m_writer, int16_t(0), &err); // rcFrame.left
  90. bx::write(m_writer, int16_t(0), &err); // rcFrame.top
  91. bx::write(m_writer, uint16_t(_width), &err); // rcFrame.right
  92. bx::write(m_writer, uint16_t(_height), &err);// rcFrame.bottom
  93. bx::write(m_writer, BX_MAKEFOURCC('s', 't', 'r', 'f'), &err);
  94. bx::write(m_writer, uint32_t(40), &err);
  95. // BITMAPINFOHEADER structure http://msdn.microsoft.com/en-us/library/windows/desktop/dd318229%28v=vs.85%29.aspx
  96. bx::write(m_writer, uint32_t(40), &err); // biSize
  97. bx::write(m_writer, _width, &err); // biWidth
  98. bx::write(m_writer, _height, &err); // biHeight
  99. bx::write(m_writer, uint16_t(1), &err); // biPlanes
  100. bx::write(m_writer, uint16_t(24), &err); // biBitCount
  101. bx::write(m_writer, uint32_t(0), &err); // biCompression
  102. bx::write(m_writer, m_frameSize, &err); // biSizeImage
  103. bx::write(m_writer, uint32_t(0), &err); // biXPelsPerMeter
  104. bx::write(m_writer, uint32_t(0), &err); // biYPelsPerMeter
  105. bx::write(m_writer, uint32_t(0), &err); // biClrUsed
  106. bx::write(m_writer, uint32_t(0), &err); // biClrImportant
  107. bx::write(m_writer, BX_MAKEFOURCC('L', 'I', 'S', 'T'), &err);
  108. m_moviListOffset = m_writer->seek();
  109. bx::write(m_writer, uint32_t(0), &err);
  110. bx::write(m_writer, BX_MAKEFOURCC('m', 'o', 'v', 'i'), &err);
  111. return true;
  112. }
  113. void close()
  114. {
  115. if (NULL != m_frame)
  116. {
  117. bx::Error err;
  118. int64_t pos = m_writer->seek();
  119. m_writer->seek(m_moviListOffset, bx::Whence::Begin);
  120. bx::write(m_writer, uint32_t(pos-m_moviListOffset-4), &err);
  121. m_writer->seek(pos, bx::Whence::Begin);
  122. bx::write(m_writer, BX_MAKEFOURCC('i', 'd', 'x', '1'), &err);
  123. bx::write(m_writer, m_numFrames*16, &err);
  124. for (uint32_t ii = 0, offset = 4; ii < m_numFrames; ++ii)
  125. {
  126. bx::write(m_writer, BX_MAKEFOURCC('0', '0', 'd', 'b'), &err);
  127. bx::write(m_writer, uint32_t(16), &err);
  128. bx::write(m_writer, offset, &err);
  129. bx::write(m_writer, m_frameSize, &err);
  130. offset += m_frameSize + 8;
  131. }
  132. pos = m_writer->seek();
  133. m_writer->seek(m_riffSizeOffset, bx::Whence::Begin);
  134. bx::write(m_writer, uint32_t(pos-m_riffSizeOffset-4), &err);
  135. m_writer->seek(m_totalFramesOffset, bx::Whence::Begin);
  136. bx::write(m_writer, m_numFrames, &err);
  137. m_writer->seek(m_lengthOffset, bx::Whence::Begin);
  138. bx::write(m_writer, m_numFrames, &err);
  139. bx::close(m_writer);
  140. delete [] m_frame;
  141. m_frame = NULL;
  142. m_frameSize = 0;
  143. }
  144. }
  145. void frame(const void* _data)
  146. {
  147. if (NULL != m_frame)
  148. {
  149. ++m_numFrames;
  150. uint32_t width = m_width;
  151. uint32_t height = m_height;
  152. uint8_t* bgr = &m_frame[8];
  153. if (m_yflip)
  154. {
  155. for (uint32_t yy = 0; yy < height; ++yy)
  156. {
  157. const uint8_t* bgra = (const uint8_t*)_data + (height-1-yy)*width*4;
  158. for (uint32_t ii = 0; ii < width; ++ii)
  159. {
  160. bgr[0] = bgra[0];
  161. bgr[1] = bgra[1];
  162. bgr[2] = bgra[2];
  163. bgr += 3;
  164. bgra += 4;
  165. }
  166. }
  167. }
  168. else
  169. {
  170. const uint8_t* bgra = (const uint8_t*)_data;
  171. for (uint32_t ii = 0, num = m_frameSize/3; ii < num; ++ii)
  172. {
  173. bgr[0] = bgra[0];
  174. bgr[1] = bgra[1];
  175. bgr[2] = bgra[2];
  176. bgr += 3;
  177. bgra += 4;
  178. }
  179. }
  180. bx::Error err;
  181. bx::write(m_writer, m_frame, m_frameSize+8, &err);
  182. }
  183. }
  184. bx::FileWriterI* m_writer;
  185. int64_t m_riffSizeOffset;
  186. int64_t m_totalFramesOffset;
  187. int64_t m_lengthOffset;
  188. int64_t m_moviListOffset;
  189. uint8_t* m_frame;
  190. uint32_t m_frameSize;
  191. uint32_t m_numFrames;
  192. uint32_t m_width;
  193. uint32_t m_height;
  194. bool m_yflip;
  195. };
  196. #endif // AVIWRITER_H_HEADER_GUARD