2013-02-21 21:07:31 -08:00
/*
2016-01-01 00:11:04 -08:00
* Copyright 2011 - 2016 Branimir Karadzic . All rights reserved .
* License : https : //github.com/bkaradzic/bgfx#license-bsd-2-clause
2013-02-21 21:07:31 -08:00
*/
2013-11-14 19:10:10 -08:00
# ifndef AVIWRITER_H_HEADER_GUARD
# define AVIWRITER_H_HEADER_GUARD
2013-02-21 21:07:31 -08:00
# include <bx/readerwriter.h>
// Simple AVI writer. VideoLAN and VirtualDub can decode it.
// Needs some bits to get jiggled to work with other players. But it's good
// enough for an example.
struct AviWriter
{
2014-05-03 15:18:28 -07:00
AviWriter ( bx : : FileWriterI * _writer )
: m_writer ( _writer )
, m_frame ( NULL )
2013-02-21 21:07:31 -08:00
, m_frameSize ( 0 )
, m_numFrames ( 0 )
, m_width ( 0 )
, m_height ( 0 )
, m_yflip ( false )
{
}
bool open ( const char * _filePath , uint32_t _width , uint32_t _height , uint32_t _fps , bool _yflip )
{
2016-01-31 16:00:02 -08:00
if ( ! bx : : open ( m_writer , _filePath ) )
2013-02-21 21:07:31 -08:00
{
return false ;
}
m_frameSize = _width * _height * 3 ;
m_frame = new uint8_t [ m_frameSize + 8 ] ;
m_numFrames = 0 ;
m_width = _width ;
m_height = _height ;
2016-01-31 16:00:02 -08:00
2013-02-21 21:07:31 -08:00
// Bgfx returns _yflip true for OpenGL since bottom left corner is 0, 0. In D3D top left corner
// is 0, 0. DIB expect OpenGL style coordinates, so this is inverted logic for AVI writer.
m_yflip = ! _yflip ;
bx : : StaticMemoryBlockWriter mem ( m_frame , 8 ) ;
// Stream Data (LIST 'movi' Chunk) http://msdn.microsoft.com/en-us/library/ms899496.aspx
bx : : write ( & mem , BX_MAKEFOURCC ( ' 0 ' , ' 0 ' , ' d ' , ' b ' ) ) ;
bx : : write ( & mem , m_frameSize ) ;
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' R ' , ' I ' , ' F ' , ' F ' ) ) ;
m_riffSizeOffset = m_writer - > seek ( ) ;
bx : : write ( m_writer , UINT32_C ( 0 ) ) ;
2013-02-21 21:07:31 -08:00
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' A ' , ' V ' , ' I ' , ' ' ) ) ;
2013-02-21 21:07:31 -08:00
// AVI RIFF Form http://msdn.microsoft.com/en-us/library/ms899422.aspx
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' L ' , ' I ' , ' S ' , ' T ' ) ) ;
bx : : write ( m_writer , UINT32_C ( 192 ) ) ;
bx : : write ( m_writer , BX_MAKEFOURCC ( ' h ' , ' d ' , ' r ' , ' l ' ) ) ;
2013-02-21 21:07:31 -08:00
// AVI Main Header http://msdn.microsoft.com/en-us/library/ms779632.aspx
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' a ' , ' v ' , ' i ' , ' h ' ) ) ;
bx : : write ( m_writer , UINT32_C ( 56 ) ) ;
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwMicroSecPerFrame
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwMaxBytesPerSec
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwPaddingGranularity
2015-11-30 21:16:11 -05:00
bx : : write ( m_writer , UINT32_C ( 0x110 ) ) ; // dwFlags
2014-05-03 15:18:28 -07:00
m_totalFramesOffset = m_writer - > seek ( ) ;
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwTotalFrames
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwInitialFrames
bx : : write ( m_writer , UINT32_C ( 1 ) ) ; // dwStreams
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwSuggestedBufferSize
bx : : write ( m_writer , _width ) ; // dwWidth
bx : : write ( m_writer , _height ) ; // dwHeight
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwReserved0
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwReserved1
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwReserved2
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwReserved3
bx : : write ( m_writer , BX_MAKEFOURCC ( ' L ' , ' I ' , ' S ' , ' T ' ) ) ;
bx : : write ( m_writer , UINT32_C ( 116 ) ) ;
bx : : write ( m_writer , BX_MAKEFOURCC ( ' s ' , ' t ' , ' r ' , ' l ' ) ) ;
2013-02-21 21:07:31 -08:00
// AVISTREAMHEADER Structure http://msdn.microsoft.com/en-us/library/ms779638.aspx
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' s ' , ' t ' , ' r ' , ' h ' ) ) ;
bx : : write ( m_writer , UINT32_C ( 56 ) ) ;
2013-02-21 21:07:31 -08:00
// AVI Stream Headers http://msdn.microsoft.com/en-us/library/ms899423.aspx
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' v ' , ' i ' , ' d ' , ' s ' ) ) ; // fccType
bx : : write ( m_writer , BX_MAKEFOURCC ( ' D ' , ' I ' , ' B ' , ' ' ) ) ; // fccHandler
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwFlags
2015-11-30 21:09:42 -05:00
bx : : write ( m_writer , uint16_t ( 0 ) ) ; // wPriority
bx : : write ( m_writer , uint16_t ( 0 ) ) ; // wLanguage
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwInitialFrames
bx : : write ( m_writer , UINT32_C ( 1 ) ) ; // dwScale
bx : : write ( m_writer , _fps ) ; // dwRate
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwStart
m_lengthOffset = m_writer - > seek ( ) ;
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwLength
bx : : write ( m_writer , m_frameSize ) ; // dwSuggestedBufferSize
bx : : write ( m_writer , UINT32_MAX ) ; // dwQuality
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // dwSampleSize
2015-11-30 21:09:42 -05:00
bx : : write ( m_writer , int16_t ( 0 ) ) ; // rcFrame.left
bx : : write ( m_writer , int16_t ( 0 ) ) ; // rcFrame.top
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , uint16_t ( _width ) ) ; // rcFrame.right
bx : : write ( m_writer , uint16_t ( _height ) ) ; // rcFrame.bottom
bx : : write ( m_writer , BX_MAKEFOURCC ( ' s ' , ' t ' , ' r ' , ' f ' ) ) ;
bx : : write ( m_writer , UINT32_C ( 40 ) ) ;
2013-02-21 21:07:31 -08:00
// BITMAPINFOHEADER structure http://msdn.microsoft.com/en-us/library/windows/desktop/dd318229%28v=vs.85%29.aspx
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , UINT32_C ( 40 ) ) ; // biSize
bx : : write ( m_writer , _width ) ; // biWidth
bx : : write ( m_writer , _height ) ; // biHeight
2015-11-30 21:09:42 -05:00
bx : : write ( m_writer , uint16_t ( 1 ) ) ; // biPlanes
bx : : write ( m_writer , uint16_t ( 24 ) ) ; // biBitCount
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // biCompression
bx : : write ( m_writer , m_frameSize ) ; // biSizeImage
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // biXPelsPerMeter
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // biYPelsPerMeter
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // biClrUsed
bx : : write ( m_writer , UINT32_C ( 0 ) ) ; // biClrImportant
bx : : write ( m_writer , BX_MAKEFOURCC ( ' L ' , ' I ' , ' S ' , ' T ' ) ) ;
m_moviListOffset = m_writer - > seek ( ) ;
bx : : write ( m_writer , UINT32_C ( 0 ) ) ;
bx : : write ( m_writer , BX_MAKEFOURCC ( ' m ' , ' o ' , ' v ' , ' i ' ) ) ;
2013-02-21 21:07:31 -08:00
return true ;
}
void close ( )
{
if ( NULL ! = m_frame )
{
2014-05-03 15:18:28 -07:00
int64_t pos = m_writer - > seek ( ) ;
m_writer - > seek ( m_moviListOffset , bx : : Whence : : Begin ) ;
bx : : write ( m_writer , uint32_t ( pos - m_moviListOffset - 4 ) ) ;
m_writer - > seek ( pos , bx : : Whence : : Begin ) ;
2013-02-21 21:07:31 -08:00
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' i ' , ' d ' , ' x ' , ' 1 ' ) ) ;
bx : : write ( m_writer , m_numFrames * 16 ) ;
2013-02-21 21:07:31 -08:00
for ( uint32_t ii = 0 , offset = 4 ; ii < m_numFrames ; + + ii )
{
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , BX_MAKEFOURCC ( ' 0 ' , ' 0 ' , ' d ' , ' b ' ) ) ;
bx : : write ( m_writer , UINT32_C ( 16 ) ) ;
bx : : write ( m_writer , offset ) ;
bx : : write ( m_writer , m_frameSize ) ;
2013-02-21 21:07:31 -08:00
offset + = m_frameSize + 8 ;
}
2014-05-03 15:18:28 -07:00
pos = m_writer - > seek ( ) ;
m_writer - > seek ( m_riffSizeOffset , bx : : Whence : : Begin ) ;
bx : : write ( m_writer , uint32_t ( pos - m_riffSizeOffset - 4 ) ) ;
2013-02-21 21:07:31 -08:00
2014-05-03 15:18:28 -07:00
m_writer - > seek ( m_totalFramesOffset , bx : : Whence : : Begin ) ;
bx : : write ( m_writer , m_numFrames ) ;
2013-02-21 21:07:31 -08:00
2014-05-03 15:18:28 -07:00
m_writer - > seek ( m_lengthOffset , bx : : Whence : : Begin ) ;
bx : : write ( m_writer , m_numFrames ) ;
2013-02-21 21:07:31 -08:00
2016-01-31 16:00:02 -08:00
bx : : close ( m_writer ) ;
2013-02-21 21:07:31 -08:00
delete [ ] m_frame ;
m_frame = NULL ;
m_frameSize = 0 ;
}
}
void frame ( const void * _data )
{
if ( NULL ! = m_frame )
{
+ + m_numFrames ;
uint32_t width = m_width ;
uint32_t height = m_height ;
uint8_t * bgr = & m_frame [ 8 ] ;
if ( m_yflip )
{
for ( uint32_t yy = 0 ; yy < height ; + + yy )
{
const uint8_t * bgra = ( const uint8_t * ) _data + ( height - 1 - yy ) * width * 4 ;
for ( uint32_t ii = 0 ; ii < width ; + + ii )
{
bgr [ 0 ] = bgra [ 0 ] ;
bgr [ 1 ] = bgra [ 1 ] ;
bgr [ 2 ] = bgra [ 2 ] ;
bgr + = 3 ;
bgra + = 4 ;
}
}
}
else
{
const uint8_t * bgra = ( const uint8_t * ) _data ;
for ( uint32_t ii = 0 , num = m_frameSize / 3 ; ii < num ; + + ii )
{
bgr [ 0 ] = bgra [ 0 ] ;
bgr [ 1 ] = bgra [ 1 ] ;
bgr [ 2 ] = bgra [ 2 ] ;
bgr + = 3 ;
bgra + = 4 ;
}
}
2014-05-03 15:18:28 -07:00
bx : : write ( m_writer , m_frame , m_frameSize + 8 ) ;
2013-02-21 21:07:31 -08:00
}
}
2014-05-03 15:18:28 -07:00
bx : : FileWriterI * m_writer ;
2013-02-21 21:07:31 -08:00
int64_t m_riffSizeOffset ;
int64_t m_totalFramesOffset ;
int64_t m_lengthOffset ;
int64_t m_moviListOffset ;
uint8_t * m_frame ;
uint32_t m_frameSize ;
uint32_t m_numFrames ;
uint32_t m_width ;
uint32_t m_height ;
bool m_yflip ;
} ;
2013-11-14 19:10:10 -08:00
# endif // AVIWRITER_H_HEADER_GUARD