Annotation of previous/src/avi_record.c, revision 1.1.1.1

1.1       root        1: /*
                      2:   Hatari - avi_record.c
                      3: 
                      4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
                      6: 
                      7:   AVI File recording
                      8: 
                      9:   This allows Hatari to record a video file, with both video and audio
                     10:   streams, at full frame rate.
                     11:   
                     12:   Video frames are saved using the current video frequency of the emulated
                     13:   machine (50 Hz, 60 Hz, 70 Hz, ...). Frames can be stored using different
                     14:   codecs. So far, supported codecs are :
                     15:    - BMP : uncompressed RGB images. Very fast to save, very few cpu needed
                     16:      but requires a lot of disk bandwidth and a lot of space.
                     17:    - PNG : compressed RBG images. Depending on the compression level, this
                     18:      can require more cpu and could slow down Hatari. As compressed images
                     19:      are much smaller than BMP images, this will require less space on disk
                     20:      and much less disk bandwidth. Compression levels 3 or 4 give could
                     21:      tradeoff between cpu usage and file size and should not slow down Hatari
                     22:      with recent computers.
                     23: 
                     24:   PNG compression will often give a x20 ratio when compared to BMP and should
                     25:   be used if you have a powerful enough cpu.
                     26: 
                     27:   Sound is saved as 16 bits pcm stereo, using the current Hatari sound output
                     28:   frequency. For best accuracy, sound frequency should be a multiple of the
                     29:   video frequency ; this means 44.1 kHz is the best choice for 50/60 Hz video.
                     30: 
                     31:   The AVI file is divided into multiple chunks. Hatari will save one video stream
                     32:   and one audio stream, so the overall strucutre of the file is the following :
                     33: 
                     34:   RIFF avi
                     35:       LIST
                     36:        hdrl
                     37:          avih
                     38:          LIST
                     39:            strl
                     40:              strh (vids)
                     41:              strf
                     42:          LIST
                     43:            strl
                     44:              strh (auds)
                     45:              strf
                     46:       LIST
                     47:        INFO
                     48:       LIST
                     49:        movi
                     50:          00db
                     51:          01wb
                     52:          ...
                     53:       idx1
                     54: */
                     55: 
                     56: const char AVIRecord_fileid[] = "Hatari avi_record.c : " __DATE__ " " __TIME__;
                     57: 
                     58: #include <SDL.h>
                     59: #include <SDL_endian.h>
                     60: 
                     61: #include "main.h"
                     62: #include "audio.h"
                     63: #include "configuration.h"
                     64: #include "log.h"
                     65: #include "screen.h"
                     66: #include "screenSnapShot.h"
                     67: #include "sound.h"
                     68: #include "statusbar.h"
                     69: #include "avi_record.h"
                     70: 
                     71: /* after above that brings in config.h */
                     72: #if HAVE_LIBPNG
                     73: #include <png.h>
                     74: #endif
                     75: 
                     76: #include "pixel_convert.h"                             /* inline functions */
                     77: 
                     78: 
                     79: 
                     80: typedef struct
                     81: {
                     82:        Uint8                   ChunkName[4];           /* '00db', '01wb', 'idx1' */
                     83:        Uint8                   ChunkSize[4];
                     84: } AVI_CHUNK;
                     85: 
                     86: 
                     87: typedef struct
                     88: {
                     89:        Uint8                   identifier[4];          /* '00db', '01wb', 'idx1' */
                     90:        Uint8                   flags[4];
                     91:        Uint8                   offset[4];
                     92:        Uint8                   length[4];
                     93: } AVI_CHUNK_INDEX;
                     94: 
                     95: 
                     96: typedef struct
                     97: {
                     98:        Uint8                   ChunkName[4];           /* 'strh' */
                     99:        Uint8                   ChunkSize[4];
                    100: 
                    101:        Uint8                   stream_type[4];         /* 'vids' or 'auds' */
                    102:        Uint8                   stream_handler[4];
                    103:        Uint8                   flags[4];
                    104:        Uint8                   priority[2];
                    105:        Uint8                   language[2];
                    106:        Uint8                   initial_frames[4];
                    107:        Uint8                   time_scale[4];
                    108:        Uint8                   data_rate[4];
                    109:        Uint8                   start_time[4];
                    110:        Uint8                   data_length[4];
                    111:        Uint8                   buffer_size[4];
                    112:        Uint8                   quality[4];
                    113:        Uint8                   sample_size[4];
                    114:        Uint8                   dest_left[2];
                    115:        Uint8                   dest_top[2];
                    116:        Uint8                   dest_right[2];
                    117:        Uint8                   dest_bottom[2];
                    118: } AVI_STREAM_HEADER;
                    119: 
                    120: 
                    121: typedef struct
                    122: {
                    123:        Uint8                   ChunkName[4];           /* 'strf' */
                    124:        Uint8                   ChunkSize[4];
                    125: 
                    126:        Uint8                   size[4];
                    127:        Uint8                   width[4];
                    128:        Uint8                   height[4];
                    129:        Uint8                   planes[2];
                    130:        Uint8                   bit_count[2];
                    131:        Uint8                   compression[4];
                    132:        Uint8                   size_image[4];
                    133:        Uint8                   xpels_meter[4];
                    134:        Uint8                   ypels_meter[4];
                    135:        Uint8                   clr_used[4];
                    136:        Uint8                   clr_important[4];
                    137: } AVI_STREAM_FORMAT_VIDS;
                    138: 
                    139: typedef struct
                    140: {
                    141:        Uint8                   ChunkName[4];           /* 'LIST' */
                    142:        Uint8                   ChunkSize[4];
                    143: 
                    144:        Uint8                   Name[4];                /* 'strl' */
                    145:        AVI_STREAM_HEADER       Header;                 /* 'strh' */
                    146:        AVI_STREAM_FORMAT_VIDS  Format;                 /* 'strf' */
                    147: } AVI_STREAM_LIST_VIDS;
                    148: 
                    149: 
                    150: typedef struct
                    151: {
                    152:        Uint8                   ChunkName[4];           /* 'strf' */
                    153:        Uint8                   ChunkSize[4];
                    154: 
                    155:        Uint8                   codec[2];
                    156:        Uint8                   channels[2];
                    157:        Uint8                   sample_rate[4];
                    158:        Uint8                   bit_rate[4];
                    159:        Uint8                   block_align[2];
                    160:        Uint8                   bits_per_sample[2];
                    161:        Uint8                   ext_size[2];
                    162: } AVI_STREAM_FORMAT_AUDS;
                    163: 
                    164: typedef struct
                    165: {
                    166:        Uint8                   ChunkName[4];           /* 'LIST' */
                    167:        Uint8                   ChunkSize[4];
                    168: 
                    169:        Uint8                   Name[4];                /* 'strl' */
                    170:        AVI_STREAM_HEADER       Header;                 /* 'strh' */
                    171:        AVI_STREAM_FORMAT_AUDS  Format;                 /* 'strf' */
                    172: } AVI_STREAM_LIST_AUDS;
                    173: 
                    174: 
                    175: typedef struct {
                    176:        Uint8                   ChunkName[4];           /* 'avih' */
                    177:        Uint8                   ChunkSize[4];
                    178: 
                    179:        Uint8                   microsec_per_frame[4];
                    180:        Uint8                   max_bytes_per_second[4];
                    181:        Uint8                   padding_granularity[4];
                    182:        Uint8                   flags[4];
                    183:        Uint8                   total_frames[4];
                    184:        Uint8                   init_frame[4];
                    185:        Uint8                   nb_streams[4];
                    186:        Uint8                   buffer_size[4];
                    187:        Uint8                   width[4];
                    188:        Uint8                   height[4];
                    189:        Uint8                   scale[4];
                    190:        Uint8                   rate[4];
                    191:        Uint8                   start[4];
                    192:        Uint8                   length[4];
                    193: } AVI_STREAM_AVIH;
                    194: 
                    195: typedef struct
                    196: {
                    197:        Uint8                   ChunkName[4];           /* 'LIST' */
                    198:        Uint8                   ChunkSize[4];
                    199: 
                    200:        Uint8                   Name[4];                /* 'hdrl' */
                    201:        AVI_STREAM_AVIH         Header;
                    202: } AVI_STREAM_LIST_AVIH;
                    203: 
                    204: 
                    205: typedef struct {
                    206:        Uint8                   ChunkName[4];           /* 'ISFT' (software used) */
                    207:        Uint8                   ChunkSize[4];
                    208: 
                    209: //     Uint8                   Text[2];                /* Text's size should be multiple of 2 (including '\0') */
                    210: } AVI_STREAM_INFO;
                    211: 
                    212: typedef struct
                    213: {
                    214:        Uint8                   ChunkName[4];           /* 'LIST' */
                    215:        Uint8                   ChunkSize[4];
                    216: 
                    217:        Uint8                   Name[4];                /* 'INFO' */
                    218:        AVI_STREAM_INFO         Info;
                    219: } AVI_STREAM_LIST_INFO;
                    220: 
                    221: 
                    222: typedef struct
                    223: {
                    224:        Uint8                   ChunkName[4];           /* 'LIST' */
                    225:        Uint8                   ChunkSize[4];
                    226: 
                    227:        Uint8                   Name[4];                /* 'movi' */
                    228: } AVI_STREAM_LIST_MOVI;
                    229: 
                    230: 
                    231: typedef struct
                    232: {
                    233:   Uint8                signature[4];                           /* 'RIFF' */
                    234:   Uint8                filesize[4];
                    235:   Uint8                type[4];                                /* 'AVI ' */
                    236:   
                    237: } RIFF_HEADER;
                    238: 
                    239: 
                    240: typedef struct {
                    241:   RIFF_HEADER                  RiffHeader;
                    242: 
                    243:   AVI_STREAM_LIST_AVIH         AviHeader;
                    244:   
                    245:   AVI_STREAM_LIST_VIDS         VideoStream;
                    246:   AVI_STREAM_LIST_AUDS         AudioStream;
                    247:   
                    248: } AVI_FILE_HEADER;
                    249: 
                    250: 
                    251: 
                    252: #define        AUDIO_STREAM_WAVE_FORMAT_PCM            0x0001
                    253: 
                    254: #define        VIDEO_STREAM_RGB                        0x00000000                      /* fourcc for BMP video frames */
                    255: #define        VIDEO_STREAM_PNG                        "MPNG"                          /* fourcc for PNG video frames */
                    256: 
                    257: #define        AVIF_HASINDEX                           0x00000010                      /* index at the end of the file */
                    258: #define        AVIF_ISINTERLEAVED                      0x00000100                      /* data are interleaved */
                    259: #define        AVIF_TRUSTCKTYPE                        0x00000800                      /* trust chunk type */
                    260: 
                    261: #define        AVIIF_KEYFRAME                          0x00000010                      /* frame is a keyframe */
                    262: 
                    263: 
                    264: typedef struct {
                    265:   /* Input params to start recording */
                    266:   int          VideoCodec;
                    267:   int          VideoCodecCompressionLevel;                                     /* 0-9 for png compression */
                    268: 
                    269:   SDL_Surface  *Surface;
                    270: 
                    271:   int          CropLeft;
                    272:   int          CropRight;
                    273:   int          CropTop;
                    274:   int          CropBottom;
                    275: 
                    276:   int          Fps;
                    277: 
                    278:   int          AudioCodec;
                    279:   int          AudioFreq;
                    280: 
                    281:   /* Internal data used by the avi recorder */
                    282:   int          Width;
                    283:   int          Height;
                    284:   int          BitCount;
                    285:   FILE         *FileOut;                               /* file to write to */
                    286:   int          TotalVideoFrames;                       /* number of recorded video frames */
                    287:   int          TotalAudioSamples;                      /* number of recorded audio samples */
                    288:   long         MoviChunkPosStart;                      /* as returned by ftell() */
                    289:   long         MoviChunkPosEnd;                        /* as returned by ftell() */
                    290: } RECORD_AVI_PARAMS;
                    291: 
                    292: 
                    293: 
                    294: bool           bRecordingAvi = false;
                    295: #if HAVE_LIBPNG
                    296: int            AviRecordDefaultVcodec = AVI_RECORD_VIDEO_CODEC_PNG;
                    297: #else
                    298: int            AviRecordDefaultVcodec = AVI_RECORD_VIDEO_CODEC_BMP;
                    299: #endif
                    300: bool           AviRecordDefaultCrop = true;
                    301: int            AviRecordDefaultFps = 50;
                    302: char           AviRecordFile[FILENAME_MAX] = "hatari.avi";
                    303: 
                    304: 
                    305: static RECORD_AVI_PARAMS       AviParams;
                    306: static AVI_FILE_HEADER         AviFileHeader;
                    307: 
                    308: 
                    309: static void    Avi_StoreU16 ( Uint8 *p , Uint16 val );
                    310: static void    Avi_StoreU32 ( Uint8 *p , Uint32 val );
                    311: static void    Avi_Store4cc ( Uint8 *p , const char *text );
                    312: static Uint32  Avi_ReadU32 ( Uint8 *p );
                    313: 
                    314: static int     Avi_GetBmpSize ( int Width , int Height , int BitCount );
                    315: 
                    316: static bool    Avi_RecordVideoStream_BMP ( RECORD_AVI_PARAMS *pAviParams );
                    317: #if HAVE_LIBPNG
                    318: static bool    Avi_RecordVideoStream_PNG ( RECORD_AVI_PARAMS *pAviParams );
                    319: #endif
                    320: static bool    Avi_RecordAudioStream_PCM ( RECORD_AVI_PARAMS *pAviParams , Sint16 pSamples[][2], int SampleIndex, int SampleLength );
                    321: 
                    322: static void    Avi_BuildFileHeader ( RECORD_AVI_PARAMS *pAviParams , AVI_FILE_HEADER *pAviFileHeader );
                    323: static bool    Avi_BuildIndex ( RECORD_AVI_PARAMS *pAviParams );
                    324: 
                    325: static bool    Avi_StartRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams , char *AviFileName );
                    326: static bool    Avi_StopRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams );
                    327: 
                    328: 
                    329: 
                    330: 
                    331: static void    Avi_StoreU16 ( Uint8 *p , Uint16 val )
                    332: {
                    333:        *p++ = val & 0xff;
                    334:        val >>= 8;
                    335:        *p = val & 0xff;
                    336: }
                    337: 
                    338: 
                    339: static void    Avi_StoreU32 ( Uint8 *p , Uint32 val )
                    340: {
                    341:        *p++ = val & 0xff;
                    342:        val >>= 8;
                    343:        *p++ = val & 0xff;
                    344:        val >>= 8;
                    345:        *p++ = val & 0xff;
                    346:        val >>= 8;
                    347:        *p = val & 0xff;
                    348: }
                    349: 
                    350: 
                    351: static void    Avi_Store4cc ( Uint8 *p , const char *text )
                    352: {
                    353:        memcpy ( p , text , 4 );
                    354: }
                    355: 
                    356: 
                    357: static Uint32  Avi_ReadU32 ( Uint8 *p )
                    358: {
                    359:        return (p[3]<<24) + (p[2]<<16) + (p[1]<<8) +p[0];
                    360: }
                    361: 
                    362: 
                    363: static int     Avi_GetBmpSize ( int Width , int Height , int BitCount )
                    364: {
                    365:        return ( Width * Height * BitCount / 8 );                                               /* bytes in one video frame */
                    366: }
                    367: 
                    368: 
                    369: 
                    370: static bool    Avi_RecordVideoStream_BMP ( RECORD_AVI_PARAMS *pAviParams )
                    371: {
                    372:        AVI_CHUNK       Chunk;
                    373:        int             SizeImage;
                    374:        Uint8           LineBuf[ 3 * pAviParams->Width ];                       /* temp buffer to convert to 24-bit BGR format */
                    375:        Uint8           *pBitmapIn , *pBitmapOut;
                    376:        int             y;
                    377:        int             NeedLock;
                    378:        
                    379:        SizeImage = Avi_GetBmpSize ( pAviParams->Width , pAviParams->Height , pAviParams->BitCount );
                    380: 
                    381:        /* Write the video frame header */
                    382:        Avi_Store4cc ( Chunk.ChunkName , "00db" );                              /* stream 0, uncompressed DIB bytes */
                    383:        Avi_StoreU32 ( Chunk.ChunkSize , SizeImage );                                   /* max size of RGB image */
                    384:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
                    385:        {
                    386:                perror ( "Avi_RecordVideoStream_BMP" );
                    387:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write bmp frame header" );
                    388:                return false;
                    389:        }
                    390: 
                    391: 
                    392:        /* Write the video frame data */
                    393:        NeedLock = SDL_MUSTLOCK( pAviParams->Surface );
                    394: 
                    395:        /* Points to the top left pixel after cropping borders */
                    396:        /* For BMP format, frame is stored from bottom to top (origin is in bottom left corner) */
                    397:        /* and bytes are in BGR order (not RGB) */
                    398:        pBitmapIn = (Uint8 *)pAviParams->Surface->pixels
                    399:                        + pAviParams->Surface->pitch * ( pAviParams->CropTop + pAviParams->Height )
                    400:                        + pAviParams->CropLeft * pAviParams->Surface->format->BytesPerPixel;
                    401: 
                    402:        for ( y=0 ; y<pAviParams->Height ; y++ )
                    403:        {
                    404:                if ( NeedLock )
                    405:                        SDL_LockSurface ( pAviParams->Surface );
                    406: 
                    407:                pBitmapOut = LineBuf;
                    408:                switch ( pAviParams->Surface->format->BytesPerPixel ) {
                    409:                        case 1 :        PixelConvert_8to24Bits_BGR(LineBuf, pBitmapIn, pAviParams->Width, pAviParams->Surface->format->palette->colors);
                    410:                                        break;
                    411:                        case 2 :        PixelConvert_16to24Bits_BGR(LineBuf, (Uint16 *)pBitmapIn, pAviParams->Width, pAviParams->Surface->format);
                    412:                                        break;
                    413:                        case 3 :        PixelConvert_24to24Bits_BGR(LineBuf, pBitmapIn, pAviParams->Width);
                    414:                                        break;
                    415:                        case 4 :        PixelConvert_32to24Bits_BGR(LineBuf, (Uint32 *)pBitmapIn, pAviParams->Width, pAviParams->Surface->format);
                    416:                                        break;
                    417:                }
                    418: 
                    419:                if ( NeedLock )
                    420:                        SDL_UnlockSurface ( pAviParams->Surface );
                    421: 
                    422:                if ( (int)fwrite ( pBitmapOut , 1 , pAviParams->Width*3 , pAviParams->FileOut ) != pAviParams->Width*3 )
                    423:                {
                    424:                        perror ( "Avi_RecordVideoStream_BMP" );
                    425:                        Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write bmp video frame" );
                    426:                        return false;
                    427:                }
                    428: 
                    429:                pBitmapIn -= pAviParams->Surface->pitch;                        /* go from bottom to top */
                    430:        }
                    431: 
                    432:        return true;
                    433: }
                    434: 
                    435: 
                    436: 
                    437: #if HAVE_LIBPNG
                    438: static bool    Avi_RecordVideoStream_PNG ( RECORD_AVI_PARAMS *pAviParams )
                    439: {
                    440:        AVI_CHUNK       Chunk;
                    441:        int             SizeImage;
                    442:        long            ChunkPos;
                    443:        Uint8   TempSize[4];
                    444:        
                    445: 
                    446:        /* Write the video frame header */
                    447:        ChunkPos = ftell ( pAviParams->FileOut );
                    448:        Avi_Store4cc ( Chunk.ChunkName , "00dc" );                              /* stream 0, compressed DIB bytes */
                    449:        Avi_StoreU32 ( Chunk.ChunkSize , 0 );                                   /* size of PNG image (-> completed later) */
                    450:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
                    451:        {
                    452:                perror ( "Avi_RecordVideoStream_PNG" );
                    453:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write png frame header" );
                    454:                return false;
                    455:        }
                    456: 
                    457: 
                    458:        /* Write the video frame data */
                    459:        SizeImage = ScreenSnapShot_SavePNG_ToFile ( pAviParams->Surface , pAviParams->FileOut ,
                    460:                pAviParams->VideoCodecCompressionLevel , PNG_FILTER_NONE ,
                    461:                pAviParams->CropLeft , pAviParams->CropRight , pAviParams->CropTop , pAviParams->CropBottom );
                    462:        if ( SizeImage <= 0 )
                    463:        {
                    464:                perror ( "Avi_RecordVideoStream_PNG" );
                    465:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write png video frame" );
                    466:                return false;
                    467:        }
                    468:        if ( SizeImage & 1 )
                    469:        {
                    470:                SizeImage++;                                                    /* add an extra '\0' byte to get an even size */
                    471:                fputc ( '\0' , pAviParams->FileOut );                           /* next chunk must be aligned on 16 bits boundary */
                    472:        }
                    473: 
                    474:        /* Update the size of the video chunk */
                    475:        Avi_StoreU32 ( TempSize , SizeImage );
                    476:        if ( fseek ( pAviParams->FileOut , ChunkPos+4 , SEEK_SET ) != 0 )
                    477:        {
                    478:                perror ( "Avi_RecordVideoStream_PNG" );
                    479:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update png frame header" );
                    480:                return false;
                    481:        }
                    482:        if ( fwrite ( TempSize , sizeof ( TempSize ) , 1 , pAviParams->FileOut ) != 1 )
                    483:        {
                    484:                perror ( "Avi_RecordVideoStream_PNG" );
                    485:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update png frame header" );
                    486:                return false;
                    487:        }
                    488: 
                    489: 
                    490:        /* Go to the end of the video frame data */
                    491:        if ( fseek ( pAviParams->FileOut , 0 , SEEK_END ) != 0 )
                    492:        {
                    493:                perror ( "Avi_RecordVideoStream_PNG" );
                    494:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to seek png video frame" );
                    495:                return false;
                    496:        }
                    497: 
                    498:        return true;
                    499: }
                    500: #endif  /* HAVE_LIBPNG */
                    501: 
                    502: 
                    503: 
                    504: bool   Avi_RecordVideoStream ( void )
                    505: {
                    506:        if ( AviParams.VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
                    507:        {
                    508:                if ( Avi_RecordVideoStream_BMP ( &AviParams ) == false )
                    509:                {
                    510:                        return false;
                    511:                }
                    512:        }
                    513: #if HAVE_LIBPNG
                    514:        else if ( AviParams.VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
                    515:        {
                    516:                if ( Avi_RecordVideoStream_PNG ( &AviParams ) == false )
                    517:                {
                    518:                        return false;
                    519:                }
                    520:        }
                    521: #endif
                    522:        else
                    523:        {
                    524:                return false;
                    525:        }
                    526: 
                    527:        AviParams.TotalVideoFrames++;
                    528:        return true;
                    529: }
                    530: 
                    531: 
                    532: 
                    533: static bool    Avi_RecordAudioStream_PCM ( RECORD_AVI_PARAMS *pAviParams , Sint16 pSamples[][2] , int SampleIndex , int SampleLength )
                    534: {
                    535:        AVI_CHUNK       Chunk;
                    536:        Sint16          sample[2];
                    537:        int             i;
                    538: 
                    539:        /* Write the audio frame header */
                    540:        Avi_Store4cc ( Chunk.ChunkName , "01wb" );                              /* stream 1, wave bytes */
                    541:        Avi_StoreU32 ( Chunk.ChunkSize , SampleLength * 4 );                    /* 16 bits, stereo -> 4 bytes */
                    542:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
                    543:        {
                    544:                perror ( "Avi_RecordAudioStream_PCM" );
                    545:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write pcm frame header" );
                    546:                return false;
                    547:        }
                    548: 
                    549:        /* Write the audio frame data */
                    550:        for ( i = 0 ; i < SampleLength; i++ )
                    551:        {
                    552:                /* Convert sample to little endian */
                    553:                sample[0] = SDL_SwapLE16 ( pSamples[ (SampleIndex+i) % MIXBUFFER_SIZE ][0]);
                    554:                sample[1] = SDL_SwapLE16 ( pSamples[ (SampleIndex+i) % MIXBUFFER_SIZE ][1]);
                    555:                /* And store */
                    556:                if ( fwrite ( &sample , sizeof ( sample ) , 1 , pAviParams->FileOut ) != 1 )
                    557:                {
                    558:                        perror ( "Avi_RecordAudioStream_PCM" );
                    559:                        Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write pcm frame" );
                    560:                        return false;
                    561:                }
                    562:        }
                    563: 
                    564:        return true;
                    565: }
                    566: 
                    567: 
                    568: 
                    569: bool   Avi_RecordAudioStream ( Sint16 pSamples[][2] , int SampleIndex , int SampleLength )
                    570: {
                    571:        if ( AviParams.AudioCodec == AVI_RECORD_AUDIO_CODEC_PCM )
                    572:        {
                    573:                if ( Avi_RecordAudioStream_PCM ( &AviParams , pSamples , SampleIndex , SampleLength ) == false )
                    574:                {
                    575:                        return false;
                    576:                }
                    577:        }
                    578:        else
                    579:        {
                    580:                return false;
                    581:        }
                    582: 
                    583:        AviParams.TotalAudioSamples += SampleLength;
                    584:        return true;
                    585: }
                    586: 
                    587: 
                    588: 
                    589: 
                    590: static void    Avi_BuildFileHeader ( RECORD_AVI_PARAMS *pAviParams , AVI_FILE_HEADER *pAviFileHeader )
                    591: {
                    592:        int     Width , Height , BitCount , Fps , SizeImage;
                    593:        int     AudioFreq;
                    594: 
                    595:        memset ( pAviFileHeader , 0 , sizeof ( *pAviFileHeader ) );
                    596: 
                    597:        Width = pAviParams->Width;
                    598:        Height =pAviParams->Height;
                    599:        BitCount = pAviParams->BitCount;
                    600:        Fps = pAviParams->Fps;
                    601:        AudioFreq = pAviParams->AudioFreq;
                    602: 
                    603:        SizeImage = 0;
                    604:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
                    605:                SizeImage = Avi_GetBmpSize ( Width , Height , BitCount );               /* size of a BMP image */
                    606:        else if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
                    607:                SizeImage = Avi_GetBmpSize ( Width , Height , BitCount );               /* max size of a PNG image */
                    608: 
                    609: 
                    610:        /* RIFF / AVI headers */
                    611:        Avi_Store4cc ( pAviFileHeader->RiffHeader.signature , "RIFF" );
                    612:        Avi_StoreU32 ( pAviFileHeader->RiffHeader.filesize , 0 );                               /* total file size (-> completed later) */
                    613:        Avi_Store4cc ( pAviFileHeader->RiffHeader.type , "AVI " );
                    614: 
                    615:        Avi_Store4cc ( pAviFileHeader->AviHeader.ChunkName , "LIST" );
                    616:        Avi_StoreU32 ( pAviFileHeader->AviHeader.ChunkSize , sizeof ( AVI_STREAM_LIST_AVIH )
                    617:                + sizeof ( AVI_STREAM_LIST_VIDS ) + sizeof ( AVI_STREAM_LIST_AUDS ) - 8 );
                    618:        Avi_Store4cc ( pAviFileHeader->AviHeader.Name , "hdrl" );
                    619: 
                    620:        Avi_Store4cc ( pAviFileHeader->AviHeader.Header.ChunkName , "avih" );
                    621:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.ChunkSize , sizeof ( AVI_STREAM_AVIH ) - 8 );
                    622:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.microsec_per_frame , 1000000 / Fps );
                    623:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.max_bytes_per_second , SizeImage * Fps + AudioFreq * 4 );
                    624:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.padding_granularity , 0 );
                    625:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.flags , AVIF_HASINDEX | AVIF_ISINTERLEAVED | AVIF_TRUSTCKTYPE );
                    626:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.total_frames , 0 );                     /* number of video frames (-> completed later) */
                    627:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.init_frame , 0 );
                    628:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.nb_streams , 2 );                       /* 1 video and 1 audio */
                    629:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.buffer_size , SizeImage );
                    630:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.width , Width );
                    631:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.height , Height );
                    632:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.scale , 0 );                            /* reserved */
                    633:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.rate , 0 );                             /* reserved */
                    634:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.start , 0 );                            /* reserved */
                    635:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.length , 0 );                           /* reserved */
                    636: 
                    637: 
                    638:        /* Video Stream */
                    639:        Avi_Store4cc ( pAviFileHeader->VideoStream.ChunkName , "LIST" );
                    640:        Avi_StoreU32 ( pAviFileHeader->VideoStream.ChunkSize , sizeof ( AVI_STREAM_LIST_VIDS ) - 8 );
                    641:        Avi_Store4cc ( pAviFileHeader->VideoStream.Name , "strl" );
                    642: 
                    643:        Avi_Store4cc ( pAviFileHeader->VideoStream.Header.ChunkName , "strh" );
                    644:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.ChunkSize , sizeof ( AVI_STREAM_HEADER ) - 8 );
                    645:        Avi_Store4cc ( pAviFileHeader->VideoStream.Header.stream_type , "vids" );
                    646:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
                    647:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.stream_handler , VIDEO_STREAM_RGB );
                    648:        else if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
                    649:                Avi_Store4cc ( pAviFileHeader->VideoStream.Header.stream_handler , VIDEO_STREAM_PNG );
                    650:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.flags , 0 );
                    651:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.priority , 0 );
                    652:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.language , 0 );
                    653:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.initial_frames , 0 );
                    654:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.time_scale , 1 );
                    655:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.data_rate , Fps );
                    656:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.start_time , 0 );
                    657:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.data_length , 0 );                    /* number of video frames (-> completed later) */
                    658:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.buffer_size , SizeImage );            /* size of an uncompressed frame */
                    659:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.quality , -1 );                       /* use default quality */
                    660:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.sample_size , 0 );                    /* 0 for video */
                    661:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_left , 0 );
                    662:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_top , 0 );
                    663:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_right , Width );
                    664:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_bottom , Height );
                    665: 
                    666:        Avi_Store4cc ( pAviFileHeader->VideoStream.Format.ChunkName , "strf" );
                    667:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.ChunkSize , sizeof ( AVI_STREAM_FORMAT_VIDS ) - 8 );
                    668:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
                    669:        {
                    670:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size , sizeof ( AVI_STREAM_FORMAT_VIDS ) - 8 );
                    671:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.width , Width );
                    672:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.height , Height );
                    673:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.planes , 1 );                 /* always 1 */
                    674:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.bit_count , BitCount );
                    675:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.compression , VIDEO_STREAM_RGB );
                    676:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size_image , SizeImage );
                    677:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.xpels_meter , 0 );
                    678:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.ypels_meter , 0 );
                    679:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_used , 0 );               /* no color map */
                    680:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_important , 0 );          /* no color map */
                    681:        }
                    682:        else if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
                    683:        {
                    684:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size , sizeof ( AVI_STREAM_FORMAT_VIDS ) - 8 );
                    685:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.width , Width );
                    686:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.height , Height );
                    687:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.planes , 1 );                 /* always 1 */
                    688:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.bit_count , BitCount );
                    689:                Avi_Store4cc ( pAviFileHeader->VideoStream.Format.compression , VIDEO_STREAM_PNG );
                    690:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size_image , SizeImage );     /* max size if uncompressed */
                    691:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.xpels_meter , 0 );
                    692:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.ypels_meter , 0 );
                    693:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_used , 0 );               /* no color map */
                    694:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_important , 0 );          /* no color map */
                    695:        }
                    696: 
                    697: 
                    698:        /* Audio Stream */
                    699:        Avi_Store4cc ( pAviFileHeader->AudioStream.ChunkName , "LIST" );
                    700:        Avi_StoreU32 ( pAviFileHeader->AudioStream.ChunkSize , sizeof ( AVI_STREAM_LIST_AUDS ) - 8 );
                    701:        Avi_Store4cc ( pAviFileHeader->AudioStream.Name , "strl" );
                    702: 
                    703:        Avi_Store4cc ( pAviFileHeader->AudioStream.Header.ChunkName , "strh" );
                    704:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.ChunkSize , sizeof ( AVI_STREAM_HEADER ) - 8 );
                    705:        Avi_Store4cc ( pAviFileHeader->AudioStream.Header.stream_type , "auds" );
                    706:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.stream_handler , 0 );                 /* not used (or could be 1 for pcm ?) */
                    707:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.flags , 0 );
                    708:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.priority , 0 );
                    709:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.language , 0 );
                    710:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.initial_frames , 0 );                 /* should be 1 in interleaved ? */
                    711:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.time_scale , 1 );
                    712:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.data_rate , AudioFreq );
                    713:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.start_time , 0 );
                    714:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.data_length , 0 );                    /* number of audio samples (-> completed later) */
                    715:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.buffer_size , AudioFreq * 4 / 50 );   /* min VBL freq is 50 Hz */
                    716:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.quality , -1 );                       /* use default quality */
                    717:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.sample_size , 4 );                    /* 2 bytes, stereo */
                    718:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_left , 0 );
                    719:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_top , 0 );
                    720:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_right , 0 );
                    721:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_bottom , 0 );
                    722: 
                    723:        Avi_Store4cc ( pAviFileHeader->AudioStream.Format.ChunkName , "strf" );
                    724:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Format.ChunkSize , sizeof ( AVI_STREAM_FORMAT_AUDS ) - 8 );
                    725:        if ( pAviParams->AudioCodec == AVI_RECORD_AUDIO_CODEC_PCM )                             /* 16 bits stereo pcm */
                    726:        {
                    727:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.codec , AUDIO_STREAM_WAVE_FORMAT_PCM );       /* 0x0001 */
                    728:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.channels , 2 );
                    729:                Avi_StoreU32 ( pAviFileHeader->AudioStream.Format.sample_rate , AudioFreq );
                    730:                Avi_StoreU32 ( pAviFileHeader->AudioStream.Format.bit_rate , AudioFreq * 2 * 2 );       /* 2 channels * 2 bytes */
                    731:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.block_align , 4 );
                    732:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.bits_per_sample , 16 );
                    733:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.ext_size , 0 );
                    734:        }
                    735: }
                    736: 
                    737: 
                    738: static bool    Avi_BuildIndex ( RECORD_AVI_PARAMS *pAviParams )
                    739: {
                    740:        AVI_CHUNK       Chunk;
                    741:        long            IndexChunkPosStart;
                    742:        long            Pos , PosWrite;
                    743:        Uint8           TempSize[4];
                    744:        AVI_CHUNK_INDEX ChunkIndex;
                    745:        Uint32          Size;
                    746: 
                    747:        fseek ( pAviParams->FileOut , 0 , SEEK_END );                           /* go to the end of the file */
                    748: 
                    749:        /* Write the 'idx1' chunk header */
                    750:        IndexChunkPosStart = ftell ( pAviParams->FileOut );
                    751:        Avi_Store4cc ( Chunk.ChunkName , "idx1" );                              /* stream 0, uncompressed DIB bytes */
                    752:        Avi_StoreU32 ( Chunk.ChunkSize , 0 );                                   /* index size (-> completed later) */
                    753:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
                    754:        {
                    755:                perror ( "Avi_BuildIndex" );
                    756:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write index header" );
                    757:                return false;
                    758:        }
                    759:        PosWrite = ftell ( pAviParams->FileOut );                               /* position to start writing indexes */
                    760: 
                    761:        /* Go to the first data chunk in the 'movi' chunk */
                    762:        fseek ( pAviParams->FileOut , pAviParams->MoviChunkPosStart + sizeof ( AVI_STREAM_LIST_MOVI ) , SEEK_SET );
                    763:        Pos = ftell ( pAviParams->FileOut );
                    764: 
                    765:        /* Build the index : we seek/read one data chunk and seek/write the */
                    766:        /* corresponding infos at the end of the index, until we reach the */
                    767:        /* end of the 'movi' chunk. */
                    768:        while ( Pos < pAviParams->MoviChunkPosEnd )
                    769:        {
                    770:                /* Read the header for this data chunk: ChunkName and ChunkSize */
                    771:                if (fread(&Chunk, sizeof(Chunk), 1, pAviParams->FileOut) != 1)
                    772:                {
                    773:                        perror("Avi_BuildIndex");
                    774:                        return false;
                    775:                }
                    776:                Size = Avi_ReadU32 ( Chunk.ChunkSize );
                    777: 
                    778:                /* Write the index infos for this chunk */
                    779:                fseek ( pAviParams->FileOut , PosWrite , SEEK_SET );
                    780:                Avi_Store4cc ( ChunkIndex.identifier , (char *)Chunk.ChunkName );       /* 00dc, 00db, 01wb, ... */
                    781:                Avi_StoreU32 ( ChunkIndex.flags , AVIIF_KEYFRAME );             /* AVIIF_KEYFRAME */
                    782:                Avi_StoreU32 ( ChunkIndex.offset , Pos - pAviParams->MoviChunkPosStart - 8  );  /* pos relative to 'movi' */
                    783:                Avi_StoreU32 ( ChunkIndex.length , Size );
                    784:                fwrite ( &ChunkIndex , sizeof ( ChunkIndex ) , 1 , pAviParams->FileOut );
                    785:                PosWrite = ftell ( pAviParams->FileOut );                       /* position for the next index */
                    786: 
                    787:                /* Go to the next data chunk in the 'movi' chunk */
                    788:                Pos = Pos + sizeof ( Chunk ) + Size;                            /* position of the next data chunk */
                    789:                fseek ( pAviParams->FileOut , Pos , SEEK_SET );
                    790:        }
                    791: 
                    792:        /* Update the size of the 'idx1' chunk */
                    793:        Avi_StoreU32 ( TempSize , PosWrite - IndexChunkPosStart - 8 );
                    794:        if ( fseek ( pAviParams->FileOut , IndexChunkPosStart+4 , SEEK_SET ) != 0 )
                    795:        {
                    796:                perror ( "Avi_BuildIndex" );
                    797:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update idx1 header" );
                    798:                return false;
                    799:        }
                    800:        if ( fwrite ( TempSize , sizeof ( TempSize ) , 1 , pAviParams->FileOut ) != 1 )
                    801:        {
                    802:                perror ( "Avi_BuildIndex" );
                    803:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update idx1 header" );
                    804:                return false;
                    805:        }
                    806: 
                    807:        return true;
                    808: }
                    809: 
                    810: 
                    811: static bool    Avi_StartRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams , char *AviFileName )
                    812: {
                    813:        AVI_STREAM_LIST_INFO    ListInfo;
                    814:        char                    InfoString[ 100 ];
                    815:        int                     Len , Len_rounded;
                    816:        AVI_STREAM_LIST_MOVI    ListMovi;
                    817: 
                    818: 
                    819:        if ( bRecordingAvi == true )                                            /* already recording ? */
                    820:                return false;
                    821: 
                    822:        /* Compute some video parameters */
                    823:        pAviParams->Width = pAviParams->Surface->w - pAviParams->CropLeft - pAviParams->CropRight;
                    824:        pAviParams->Height = pAviParams->Surface->h - pAviParams->CropTop - pAviParams->CropBottom;
                    825:        pAviParams->BitCount = 24;
                    826:        
                    827: #if !HAVE_LIBPNG
                    828:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
                    829:        {
                    830:                perror ( "AviStartRecording" );
                    831:                Log_AlertDlg ( LOG_ERROR, "AVI recording : Hatari was not built with libpng support" );
                    832:                return false;
                    833:        }
                    834: #endif
                    835: 
                    836:        /* Open the file */
                    837:        pAviParams->FileOut = fopen ( AviFileName , "wb+" );
                    838:        if ( !pAviParams->FileOut )
                    839:        {
                    840:                perror ( "AviStartRecording" );
                    841:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to open file" );
                    842:                return false;
                    843:        }
                    844: 
                    845:        /* Build the AVI header */
                    846:        Avi_BuildFileHeader ( pAviParams , &AviFileHeader );
                    847:        
                    848:        /* Write the AVI header */
                    849:        if ( fwrite ( &AviFileHeader , sizeof ( AviFileHeader ) , 1 , pAviParams->FileOut ) != 1 )
                    850:        {
                    851:                perror ( "AviStartRecording" );
                    852:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write avi header" );
                    853:                return false;
                    854:        }
                    855: 
                    856:        /* Write the INFO header */
                    857:        memset ( InfoString , 0 , sizeof ( InfoString ) );
                    858:        Len = snprintf ( InfoString , sizeof ( InfoString ) , "%s - the Atari ST, STE, TT and Falcon emulator" , PROG_NAME ) + 1;
                    859:        Len_rounded = Len + ( Len % 2 == 0 ? 0 : 1 );                           /* round Len to the next multiple of 2 */
                    860:        Avi_Store4cc ( ListInfo.ChunkName , "LIST" );
                    861:        Avi_StoreU32 ( ListInfo.ChunkSize , sizeof ( AVI_STREAM_LIST_INFO ) - 8 + Len_rounded );
                    862:        Avi_Store4cc ( ListInfo.Name , "INFO" );
                    863:        Avi_Store4cc ( ListInfo.Info.ChunkName , "ISFT" );
                    864:        Avi_StoreU32 ( ListInfo.Info.ChunkSize , Len );
                    865:        if ( fwrite ( &ListInfo , sizeof ( ListInfo ) , 1 , pAviParams->FileOut ) != 1 )
                    866:        {
                    867:                perror ( "AviStartRecording" );
                    868:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write info header" );
                    869:                return false;
                    870:        }
                    871:        /* Write the info string + '\0' and write an optionnal extra '\0' byte to get a total multiple of 2 */
                    872:        if ( fwrite ( InfoString , Len_rounded , 1 , pAviParams->FileOut ) != 1 )
                    873:        {
                    874:                perror ( "AviStartRecording" );
                    875:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write info header" );
                    876:                return false;
                    877:        }
                    878: 
                    879:        /* Write the MOVI header */
                    880:        Avi_Store4cc ( ListMovi.ChunkName , "LIST" );
                    881:        Avi_StoreU32 ( ListMovi.ChunkSize , 0 );                                        /* completed when recording stops */
                    882:        Avi_Store4cc ( ListMovi.Name , "movi" );
                    883:        pAviParams->MoviChunkPosStart = ftell ( pAviParams->FileOut );
                    884:        if ( fwrite ( &ListMovi , sizeof ( ListMovi ) , 1 , pAviParams->FileOut ) != 1 )
                    885:        {
                    886:                perror ( "AviStartRecording" );
                    887:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write movi header" );
                    888:                return false;
                    889:        }
                    890: 
                    891: 
                    892:        /* We're ok to record */
                    893:        Log_AlertDlg ( LOG_INFO, "AVI recording has been started");
                    894:        bRecordingAvi = true;
                    895: 
                    896:        return true;
                    897: }
                    898: 
                    899: 
                    900: 
                    901: static bool    Avi_StopRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams )
                    902: {
                    903:        long    FileSize;
                    904:        Uint8   TempSize[4];
                    905: 
                    906: 
                    907:        if ( bRecordingAvi == false )                                           /* no recording ? */
                    908:                return true;
                    909: 
                    910:        /* Update the size of the 'movi' chunk */
                    911:        fseek ( pAviParams->FileOut , 0 , SEEK_END );                           /* go to the end of the 'movi' chunk */
                    912:        pAviParams->MoviChunkPosEnd = ftell ( pAviParams->FileOut );
                    913:        Avi_StoreU32 ( TempSize , pAviParams->MoviChunkPosEnd - pAviParams->MoviChunkPosStart - 8 );
                    914: 
                    915:        if ( fseek ( pAviParams->FileOut , pAviParams->MoviChunkPosStart+4 , SEEK_SET ) != 0 )
                    916:        {
                    917:                perror ( "AviStopRecording" );
                    918:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update movi header" );
                    919:                return false;
                    920:        }
                    921:        if ( fwrite ( TempSize , sizeof ( TempSize ) , 1 , pAviParams->FileOut ) != 1 )
                    922:        {
                    923:                perror ( "AviStopRecording" );
                    924:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update movi header" );
                    925:                return false;
                    926:        }
                    927: 
                    928:        /* Build the index chunk */
                    929:        if ( ! Avi_BuildIndex ( pAviParams ) )
                    930:        {
                    931:                perror ( "AviStopRecording" );
                    932:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to build index" );
                    933:                return false;
                    934:        }
                    935:        
                    936:        /* Update the avi header (file size, number of output frames, ...) */
                    937:        fseek ( pAviParams->FileOut , 0 , SEEK_END );                           /* go to the end of the file */
                    938:        FileSize = ftell ( pAviParams->FileOut );
                    939: 
                    940:        Avi_StoreU32 ( AviFileHeader.RiffHeader.filesize , FileSize - 8 );      /* 32 bits, limited to 4GB */
                    941:        Avi_StoreU32 ( AviFileHeader.AviHeader.Header.total_frames , pAviParams->TotalVideoFrames );    /* number of video frames */
                    942:        Avi_StoreU32 ( AviFileHeader.VideoStream.Header.data_length , pAviParams->TotalVideoFrames );   /* number of video frames */
                    943:        Avi_StoreU32 ( AviFileHeader.AudioStream.Header.data_length , pAviParams->TotalAudioSamples );  /* number of audio samples */
                    944: 
                    945:        if ( fseek ( pAviParams->FileOut , 0 , SEEK_SET ) != 0 )
                    946:        {
                    947:                perror ( "AviStopRecording" );
                    948:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update avi header" );
                    949:                return false;
                    950:        }
                    951:        if ( fwrite ( &AviFileHeader , sizeof ( AviFileHeader ) , 1 , pAviParams->FileOut ) != 1 )
                    952:        {
                    953:                perror ( "AviStopRecording" );
                    954:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update avi header" );
                    955:                return false;
                    956:        }
                    957: 
                    958: 
                    959:        /* Close the file */
                    960:        fclose ( pAviParams->FileOut );
                    961: 
                    962:        Log_AlertDlg ( LOG_INFO, "AVI recording has been stopped");
                    963:        bRecordingAvi = false;
                    964: 
                    965:        return true;
                    966: }
                    967: 
                    968: 
                    969: 
                    970: 
                    971: /*-----------------------------------------------------------------------*/
                    972: /**
                    973:  * Are we recording an AVI ?
                    974:  */
                    975: bool   Avi_AreWeRecording ( void )
                    976: {
                    977:         return bRecordingAvi;
                    978: }
                    979: 
                    980: 
                    981: 
                    982: bool   Avi_StartRecording ( char *FileName , bool CropGui , int Fps , int VideoCodec )
                    983: {
                    984:        memset ( &AviParams , 0 , sizeof ( AviParams ) );
                    985: 
                    986:        AviParams.VideoCodec = VideoCodec;
                    987:        AviParams.VideoCodecCompressionLevel = 9;       /* png compression level */
                    988:        AviParams.AudioCodec = AVI_RECORD_AUDIO_CODEC_PCM;
                    989:        AviParams.AudioFreq = ConfigureParams.Sound.nPlaybackFreq;
                    990:        AviParams.Surface = sdlscrn;
                    991:        AviParams.Fps = Fps;
                    992: 
                    993:        if ( !CropGui )                                 /* Keep gui's status bar */
                    994:        {
                    995:                AviParams.CropLeft = 0;
                    996:                AviParams.CropRight = 0;
                    997:                AviParams.CropTop = 0;
                    998:                AviParams.CropBottom = 0;
                    999:        }
                   1000:        else                                            /* Record only the content of the Atari's screen */
                   1001:        {
                   1002:                AviParams.CropLeft = 0;
                   1003:                AviParams.CropRight = 0;
                   1004:                AviParams.CropTop = 0;
                   1005:                AviParams.CropBottom = Statusbar_GetHeight();
                   1006:        }
                   1007:        
                   1008:        
                   1009:        return Avi_StartRecording_WithParams ( &AviParams , FileName );
                   1010: }
                   1011: 
                   1012: 
                   1013: bool   Avi_StopRecording ( void )
                   1014: {
                   1015:        return Avi_StopRecording_WithParams ( &AviParams );
                   1016: }
                   1017: 
                   1018: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.