Annotation of previous_trunk/src/avi_record.c, revision 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 good
        !            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 structure 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;                                    /* Fps << 24 */
        !           277:   int          Fps_scale;                              /* 1 << 24 */
        !           278: 
        !           279:   int          AudioCodec;
        !           280:   int          AudioFreq;
        !           281: 
        !           282:   /* Internal data used by the avi recorder */
        !           283:   int          Width;
        !           284:   int          Height;
        !           285:   int          BitCount;
        !           286:   FILE         *FileOut;                               /* file to write to */
        !           287:   int          TotalVideoFrames;                       /* number of recorded video frames */
        !           288:   int          TotalAudioSamples;                      /* number of recorded audio samples */
        !           289:   long         MoviChunkPosStart;                      /* as returned by ftell() */
        !           290:   long         MoviChunkPosEnd;                        /* as returned by ftell() */
        !           291: } RECORD_AVI_PARAMS;
        !           292: 
        !           293: 
        !           294: 
        !           295: bool           bRecordingAvi = false;
        !           296: 
        !           297: static RECORD_AVI_PARAMS       AviParams;
        !           298: static AVI_FILE_HEADER         AviFileHeader;
        !           299: 
        !           300: 
        !           301: static void    Avi_StoreU16 ( Uint8 *p , Uint16 val );
        !           302: static void    Avi_StoreU32 ( Uint8 *p , Uint32 val );
        !           303: static void    Avi_Store4cc ( Uint8 *p , const char *text );
        !           304: static Uint32  Avi_ReadU32 ( Uint8 *p );
        !           305: 
        !           306: static int     Avi_GetBmpSize ( int Width , int Height , int BitCount );
        !           307: 
        !           308: static bool    Avi_RecordVideoStream_BMP ( RECORD_AVI_PARAMS *pAviParams );
        !           309: #if HAVE_LIBPNG
        !           310: static bool    Avi_RecordVideoStream_PNG ( RECORD_AVI_PARAMS *pAviParams );
        !           311: #endif
        !           312: static bool    Avi_RecordAudioStream_PCM ( RECORD_AVI_PARAMS *pAviParams , Sint16 pSamples[][2], int SampleIndex, int SampleLength );
        !           313: 
        !           314: static void    Avi_BuildFileHeader ( RECORD_AVI_PARAMS *pAviParams , AVI_FILE_HEADER *pAviFileHeader );
        !           315: static bool    Avi_BuildIndex ( RECORD_AVI_PARAMS *pAviParams );
        !           316: 
        !           317: static bool    Avi_StartRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams , char *AviFileName );
        !           318: static bool    Avi_StopRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams );
        !           319: 
        !           320: 
        !           321: 
        !           322: 
        !           323: static void    Avi_StoreU16 ( Uint8 *p , Uint16 val )
        !           324: {
        !           325:        *p++ = val & 0xff;
        !           326:        val >>= 8;
        !           327:        *p = val & 0xff;
        !           328: }
        !           329: 
        !           330: 
        !           331: static void    Avi_StoreU32 ( Uint8 *p , Uint32 val )
        !           332: {
        !           333:        *p++ = val & 0xff;
        !           334:        val >>= 8;
        !           335:        *p++ = val & 0xff;
        !           336:        val >>= 8;
        !           337:        *p++ = val & 0xff;
        !           338:        val >>= 8;
        !           339:        *p = val & 0xff;
        !           340: }
        !           341: 
        !           342: 
        !           343: static void    Avi_Store4cc ( Uint8 *p , const char *text )
        !           344: {
        !           345:        memcpy ( p , text , 4 );
        !           346: }
        !           347: 
        !           348: 
        !           349: static Uint32  Avi_ReadU32 ( Uint8 *p )
        !           350: {
        !           351:        return (p[3]<<24) + (p[2]<<16) + (p[1]<<8) +p[0];
        !           352: }
        !           353: 
        !           354: 
        !           355: static int     Avi_GetBmpSize ( int Width , int Height , int BitCount )
        !           356: {
        !           357:        return ( Width * Height * BitCount / 8 );                                               /* bytes in one video frame */
        !           358: }
        !           359: 
        !           360: 
        !           361: 
        !           362: static bool    Avi_RecordVideoStream_BMP ( RECORD_AVI_PARAMS *pAviParams )
        !           363: {
        !           364:        AVI_CHUNK       Chunk;
        !           365:        int             SizeImage;
        !           366:        Uint8           LineBuf[ 3 * pAviParams->Width ];                       /* temp buffer to convert to 24-bit BGR format */
        !           367:        Uint8           *pBitmapIn , *pBitmapOut;
        !           368:        int             y;
        !           369:        int             NeedLock;
        !           370:        
        !           371:        SizeImage = Avi_GetBmpSize ( pAviParams->Width , pAviParams->Height , pAviParams->BitCount );
        !           372: 
        !           373:        /* Write the video frame header */
        !           374:        Avi_Store4cc ( Chunk.ChunkName , "00db" );                              /* stream 0, uncompressed DIB bytes */
        !           375:        Avi_StoreU32 ( Chunk.ChunkSize , SizeImage );                                   /* max size of RGB image */
        !           376:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
        !           377:        {
        !           378:                perror ( "Avi_RecordVideoStream_BMP" );
        !           379:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write bmp frame header" );
        !           380:                return false;
        !           381:        }
        !           382: 
        !           383: 
        !           384:        /* Write the video frame data */
        !           385:        NeedLock = SDL_MUSTLOCK( pAviParams->Surface );
        !           386: 
        !           387:        /* Points to the top left pixel after cropping borders */
        !           388:        /* For BMP format, frame is stored from bottom to top (origin is in bottom left corner) */
        !           389:        /* and bytes are in BGR order (not RGB) */
        !           390:        pBitmapIn = (Uint8 *)pAviParams->Surface->pixels
        !           391:                        + pAviParams->Surface->pitch * ( pAviParams->CropTop + pAviParams->Height )
        !           392:                        + pAviParams->CropLeft * pAviParams->Surface->format->BytesPerPixel;
        !           393: 
        !           394:        for ( y=0 ; y<pAviParams->Height ; y++ )
        !           395:        {
        !           396:                if ( NeedLock )
        !           397:                        SDL_LockSurface ( pAviParams->Surface );
        !           398: 
        !           399:                pBitmapOut = LineBuf;
        !           400:                switch ( pAviParams->Surface->format->BytesPerPixel ) {
        !           401:                        case 1 :        PixelConvert_8to24Bits_BGR(LineBuf, pBitmapIn, pAviParams->Width, pAviParams->Surface->format->palette->colors);
        !           402:                                        break;
        !           403:                        case 2 :        PixelConvert_16to24Bits_BGR(LineBuf, (Uint16 *)pBitmapIn, pAviParams->Width, pAviParams->Surface->format);
        !           404:                                        break;
        !           405:                        case 3 :        PixelConvert_24to24Bits_BGR(LineBuf, pBitmapIn, pAviParams->Width);
        !           406:                                        break;
        !           407:                        case 4 :        PixelConvert_32to24Bits_BGR(LineBuf, (Uint32 *)pBitmapIn, pAviParams->Width, pAviParams->Surface->format);
        !           408:                                        break;
        !           409:                }
        !           410: 
        !           411:                if ( NeedLock )
        !           412:                        SDL_UnlockSurface ( pAviParams->Surface );
        !           413: 
        !           414:                if ( (int)fwrite ( pBitmapOut , 1 , pAviParams->Width*3 , pAviParams->FileOut ) != pAviParams->Width*3 )
        !           415:                {
        !           416:                        perror ( "Avi_RecordVideoStream_BMP" );
        !           417:                        Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write bmp video frame" );
        !           418:                        return false;
        !           419:                }
        !           420: 
        !           421:                pBitmapIn -= pAviParams->Surface->pitch;                        /* go from bottom to top */
        !           422:        }
        !           423: 
        !           424:        return true;
        !           425: }
        !           426: 
        !           427: 
        !           428: 
        !           429: #if HAVE_LIBPNG
        !           430: static bool    Avi_RecordVideoStream_PNG ( RECORD_AVI_PARAMS *pAviParams )
        !           431: {
        !           432:        AVI_CHUNK       Chunk;
        !           433:        int             SizeImage;
        !           434:        long            ChunkPos;
        !           435:        Uint8   TempSize[4];
        !           436:        
        !           437: 
        !           438:        /* Write the video frame header */
        !           439:        ChunkPos = ftell ( pAviParams->FileOut );
        !           440:        Avi_Store4cc ( Chunk.ChunkName , "00dc" );                              /* stream 0, compressed DIB bytes */
        !           441:        Avi_StoreU32 ( Chunk.ChunkSize , 0 );                                   /* size of PNG image (-> completed later) */
        !           442:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
        !           443:         goto png_error;
        !           444:     
        !           445:        /* Write the video frame data */
        !           446:        SizeImage = ScreenSnapShot_SavePNG_ToFile ( pAviParams->Surface , pAviParams->FileOut ,
        !           447:                                                pAviParams->VideoCodecCompressionLevel , PNG_FILTER_NONE ,
        !           448:                                                pAviParams->CropLeft , pAviParams->CropRight , pAviParams->CropTop , pAviParams->CropBottom );
        !           449:        if ( SizeImage <= 0 )
        !           450:         goto png_error;
        !           451:        if ( SizeImage & 1 )
        !           452:        {
        !           453:                SizeImage++;                                                    /* add an extra '\0' byte to get an even size */
        !           454:                fputc ( '\0' , pAviParams->FileOut );                           /* next chunk must be aligned on 16 bits boundary */
        !           455:        }
        !           456: 
        !           457:        /* Update the size of the video chunk */
        !           458:        Avi_StoreU32 ( TempSize , SizeImage );
        !           459:        if ( fseek ( pAviParams->FileOut , ChunkPos+4 , SEEK_SET ) != 0 )
        !           460:         goto png_error;
        !           461:        if ( fwrite ( TempSize , sizeof ( TempSize ) , 1 , pAviParams->FileOut ) != 1 )
        !           462:         goto png_error;
        !           463:     
        !           464:        /* Go to the end of the video frame data */
        !           465:        if ( fseek ( pAviParams->FileOut , 0 , SEEK_END ) != 0 )
        !           466:         goto png_error;
        !           467:        return true;
        !           468: 
        !           469: png_error:
        !           470:     perror ( "Avi_RecordVideoStream_PNG" );
        !           471:     Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write png frame" );
        !           472:     return false;
        !           473: }
        !           474: #endif  /* HAVE_LIBPNG */
        !           475: 
        !           476: 
        !           477: 
        !           478: bool   Avi_RecordVideoStream ( void )
        !           479: {
        !           480:        if ( AviParams.VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
        !           481:        {
        !           482:                if ( Avi_RecordVideoStream_BMP ( &AviParams ) == false )
        !           483:                {
        !           484:                        return false;
        !           485:                }
        !           486:        }
        !           487: #if HAVE_LIBPNG
        !           488:        else if ( AviParams.VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
        !           489:        {
        !           490:                if ( Avi_RecordVideoStream_PNG ( &AviParams ) == false )
        !           491:                {
        !           492:                        return false;
        !           493:                }
        !           494:        }
        !           495: #endif
        !           496:        else
        !           497:        {
        !           498:                return false;
        !           499:        }
        !           500: 
        !           501:     if (++AviParams.TotalVideoFrames % ( AviParams.Fps / AviParams.Fps_scale ) == 0)
        !           502:     {
        !           503:         int secs = AviParams.TotalVideoFrames / ( AviParams.Fps / AviParams.Fps_scale );
        !           504:         char str[6] = "00:00";
        !           505:         str[0] = '0' + (secs / 60) / 10;
        !           506:         str[1] = '0' + (secs / 60) % 10;
        !           507:         str[3] = '0' + (secs % 60) / 10;
        !           508:         str[4] = '0' + (secs % 60) % 10;
        !           509:         Main_SetTitle(str);
        !           510:     }
        !           511:        return true;
        !           512: }
        !           513: 
        !           514: 
        !           515: 
        !           516: static bool    Avi_RecordAudioStream_PCM ( RECORD_AVI_PARAMS *pAviParams , Sint16 pSamples[][2] , int SampleIndex , int SampleLength )
        !           517: {
        !           518:        AVI_CHUNK       Chunk;
        !           519:        Sint16          sample[2];
        !           520:        int             i;
        !           521: 
        !           522:        /* Write the audio frame header */
        !           523:        Avi_Store4cc ( Chunk.ChunkName , "01wb" );                              /* stream 1, wave bytes */
        !           524:        Avi_StoreU32 ( Chunk.ChunkSize , SampleLength * 4 );                    /* 16 bits, stereo -> 4 bytes */
        !           525:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
        !           526:        {
        !           527:                perror ( "Avi_RecordAudioStream_PCM" );
        !           528:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write pcm frame header" );
        !           529:                return false;
        !           530:        }
        !           531: 
        !           532:        /* Write the audio frame data */
        !           533:        for ( i = 0 ; i < SampleLength; i++ )
        !           534:        {
        !           535:                /* Convert sample to little endian */
        !           536:                sample[0] = SDL_SwapLE16 ( pSamples[ (SampleIndex+i) % MIXBUFFER_SIZE ][0]);
        !           537:                sample[1] = SDL_SwapLE16 ( pSamples[ (SampleIndex+i) % MIXBUFFER_SIZE ][1]);
        !           538:                /* And store */
        !           539:                if ( fwrite ( &sample , sizeof ( sample ) , 1 , pAviParams->FileOut ) != 1 )
        !           540:                {
        !           541:                        perror ( "Avi_RecordAudioStream_PCM" );
        !           542:                        Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write pcm frame" );
        !           543:                        return false;
        !           544:                }
        !           545:        }
        !           546: 
        !           547:        return true;
        !           548: }
        !           549: 
        !           550: 
        !           551: 
        !           552: bool   Avi_RecordAudioStream ( Sint16 pSamples[][2] , int SampleIndex , int SampleLength )
        !           553: {
        !           554:        if ( AviParams.AudioCodec == AVI_RECORD_AUDIO_CODEC_PCM )
        !           555:        {
        !           556:                if ( Avi_RecordAudioStream_PCM ( &AviParams , pSamples , SampleIndex , SampleLength ) == false )
        !           557:                {
        !           558:                        return false;
        !           559:                }
        !           560:        }
        !           561:        else
        !           562:        {
        !           563:                return false;
        !           564:        }
        !           565: 
        !           566:        AviParams.TotalAudioSamples += SampleLength;
        !           567:        return true;
        !           568: }
        !           569: 
        !           570: 
        !           571: 
        !           572: 
        !           573: static void    Avi_BuildFileHeader ( RECORD_AVI_PARAMS *pAviParams , AVI_FILE_HEADER *pAviFileHeader )
        !           574: {
        !           575:        int     Width , Height , BitCount , Fps , Fps_scale , SizeImage;
        !           576:        int     AudioFreq;
        !           577: 
        !           578:        memset ( pAviFileHeader , 0 , sizeof ( *pAviFileHeader ) );
        !           579: 
        !           580:        Width = pAviParams->Width;
        !           581:        Height =pAviParams->Height;
        !           582:        BitCount = pAviParams->BitCount;
        !           583:        Fps = pAviParams->Fps;
        !           584:     Fps_scale = pAviParams->Fps_scale;
        !           585:        AudioFreq = pAviParams->AudioFreq;
        !           586: 
        !           587:        SizeImage = 0;
        !           588:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
        !           589:                SizeImage = Avi_GetBmpSize ( Width , Height , BitCount );               /* size of a BMP image */
        !           590:        else if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
        !           591:                SizeImage = Avi_GetBmpSize ( Width , Height , BitCount );               /* max size of a PNG image */
        !           592: 
        !           593: 
        !           594:        /* RIFF / AVI headers */
        !           595:        Avi_Store4cc ( pAviFileHeader->RiffHeader.signature , "RIFF" );
        !           596:        Avi_StoreU32 ( pAviFileHeader->RiffHeader.filesize , 0 );                               /* total file size (-> completed later) */
        !           597:        Avi_Store4cc ( pAviFileHeader->RiffHeader.type , "AVI " );
        !           598: 
        !           599:        Avi_Store4cc ( pAviFileHeader->AviHeader.ChunkName , "LIST" );
        !           600:        Avi_StoreU32 ( pAviFileHeader->AviHeader.ChunkSize , sizeof ( AVI_STREAM_LIST_AVIH )
        !           601:                + sizeof ( AVI_STREAM_LIST_VIDS ) + sizeof ( AVI_STREAM_LIST_AUDS ) - 8 );
        !           602:        Avi_Store4cc ( pAviFileHeader->AviHeader.Name , "hdrl" );
        !           603: 
        !           604:        Avi_Store4cc ( pAviFileHeader->AviHeader.Header.ChunkName , "avih" );
        !           605:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.ChunkSize , sizeof ( AVI_STREAM_AVIH ) - 8 );
        !           606:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.microsec_per_frame , (Uint32)( ( 1000000 * (Sint64)Fps_scale ) / Fps ) );
        !           607:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.max_bytes_per_second , (Uint32)( ( (Sint64)SizeImage * Fps ) / Fps_scale + AudioFreq * 4 ) );
        !           608:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.padding_granularity , 0 );
        !           609:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.flags , AVIF_HASINDEX | AVIF_ISINTERLEAVED | AVIF_TRUSTCKTYPE );
        !           610:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.total_frames , 0 );                     /* number of video frames (-> completed later) */
        !           611:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.init_frame , 0 );
        !           612:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.nb_streams , 2 );                       /* 1 video and 1 audio */
        !           613:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.buffer_size , SizeImage );
        !           614:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.width , Width );
        !           615:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.height , Height );
        !           616:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.scale , 0 );                            /* reserved */
        !           617:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.rate , 0 );                             /* reserved */
        !           618:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.start , 0 );                            /* reserved */
        !           619:        Avi_StoreU32 ( pAviFileHeader->AviHeader.Header.length , 0 );                           /* reserved */
        !           620: 
        !           621: 
        !           622:        /* Video Stream */
        !           623:        Avi_Store4cc ( pAviFileHeader->VideoStream.ChunkName , "LIST" );
        !           624:        Avi_StoreU32 ( pAviFileHeader->VideoStream.ChunkSize , sizeof ( AVI_STREAM_LIST_VIDS ) - 8 );
        !           625:        Avi_Store4cc ( pAviFileHeader->VideoStream.Name , "strl" );
        !           626: 
        !           627:        Avi_Store4cc ( pAviFileHeader->VideoStream.Header.ChunkName , "strh" );
        !           628:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.ChunkSize , sizeof ( AVI_STREAM_HEADER ) - 8 );
        !           629:        Avi_Store4cc ( pAviFileHeader->VideoStream.Header.stream_type , "vids" );
        !           630:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
        !           631:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.stream_handler , VIDEO_STREAM_RGB );
        !           632:        else if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
        !           633:                Avi_Store4cc ( pAviFileHeader->VideoStream.Header.stream_handler , VIDEO_STREAM_PNG );
        !           634:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.flags , 0 );
        !           635:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.priority , 0 );
        !           636:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.language , 0 );
        !           637:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.initial_frames , 0 );
        !           638:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.time_scale , Fps_scale );
        !           639:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.data_rate , Fps );
        !           640:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.start_time , 0 );
        !           641:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.data_length , 0 );                    /* number of video frames (-> completed later) */
        !           642:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.buffer_size , SizeImage );            /* size of an uncompressed frame */
        !           643:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.quality , -1 );                       /* use default quality */
        !           644:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Header.sample_size , 0 );                    /* 0 for video */
        !           645:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_left , 0 );
        !           646:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_top , 0 );
        !           647:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_right , Width );
        !           648:        Avi_StoreU16 ( pAviFileHeader->VideoStream.Header.dest_bottom , Height );
        !           649: 
        !           650:        Avi_Store4cc ( pAviFileHeader->VideoStream.Format.ChunkName , "strf" );
        !           651:        Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.ChunkSize , sizeof ( AVI_STREAM_FORMAT_VIDS ) - 8 );
        !           652:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_BMP )
        !           653:        {
        !           654:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size , sizeof ( AVI_STREAM_FORMAT_VIDS ) - 8 );
        !           655:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.width , Width );
        !           656:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.height , Height );
        !           657:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.planes , 1 );                 /* always 1 */
        !           658:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.bit_count , BitCount );
        !           659:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.compression , VIDEO_STREAM_RGB );
        !           660:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size_image , SizeImage );
        !           661:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.xpels_meter , 0 );
        !           662:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.ypels_meter , 0 );
        !           663:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_used , 0 );               /* no color map */
        !           664:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_important , 0 );          /* no color map */
        !           665:        }
        !           666:        else if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
        !           667:        {
        !           668:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size , sizeof ( AVI_STREAM_FORMAT_VIDS ) - 8 );
        !           669:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.width , Width );
        !           670:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.height , Height );
        !           671:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.planes , 1 );                 /* always 1 */
        !           672:                Avi_StoreU16 ( pAviFileHeader->VideoStream.Format.bit_count , BitCount );
        !           673:                Avi_Store4cc ( pAviFileHeader->VideoStream.Format.compression , VIDEO_STREAM_PNG );
        !           674:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.size_image , SizeImage );     /* max size if uncompressed */
        !           675:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.xpels_meter , 0 );
        !           676:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.ypels_meter , 0 );
        !           677:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_used , 0 );               /* no color map */
        !           678:                Avi_StoreU32 ( pAviFileHeader->VideoStream.Format.clr_important , 0 );          /* no color map */
        !           679:        }
        !           680: 
        !           681: 
        !           682:        /* Audio Stream */
        !           683:        Avi_Store4cc ( pAviFileHeader->AudioStream.ChunkName , "LIST" );
        !           684:        Avi_StoreU32 ( pAviFileHeader->AudioStream.ChunkSize , sizeof ( AVI_STREAM_LIST_AUDS ) - 8 );
        !           685:        Avi_Store4cc ( pAviFileHeader->AudioStream.Name , "strl" );
        !           686: 
        !           687:        Avi_Store4cc ( pAviFileHeader->AudioStream.Header.ChunkName , "strh" );
        !           688:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.ChunkSize , sizeof ( AVI_STREAM_HEADER ) - 8 );
        !           689:        Avi_Store4cc ( pAviFileHeader->AudioStream.Header.stream_type , "auds" );
        !           690:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.stream_handler , 0 );                 /* not used (or could be 1 for pcm ?) */
        !           691:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.flags , 0 );
        !           692:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.priority , 0 );
        !           693:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.language , 0 );
        !           694:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.initial_frames , 0 );                 /* should be 1 in interleaved ? */
        !           695:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.time_scale , 1 );
        !           696:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.data_rate , AudioFreq );
        !           697:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.start_time , 0 );
        !           698:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.data_length , 0 );                    /* number of audio samples (-> completed later) */
        !           699:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.buffer_size , AudioFreq * 4 / 50 );   /* min VBL freq is 50 Hz */
        !           700:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.quality , -1 );                       /* use default quality */
        !           701:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Header.sample_size , 4 );                    /* 2 bytes, stereo */
        !           702:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_left , 0 );
        !           703:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_top , 0 );
        !           704:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_right , 0 );
        !           705:        Avi_StoreU16 ( pAviFileHeader->AudioStream.Header.dest_bottom , 0 );
        !           706: 
        !           707:        Avi_Store4cc ( pAviFileHeader->AudioStream.Format.ChunkName , "strf" );
        !           708:        Avi_StoreU32 ( pAviFileHeader->AudioStream.Format.ChunkSize , sizeof ( AVI_STREAM_FORMAT_AUDS ) - 8 );
        !           709:        if ( pAviParams->AudioCodec == AVI_RECORD_AUDIO_CODEC_PCM )                             /* 16 bits stereo pcm */
        !           710:        {
        !           711:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.codec , AUDIO_STREAM_WAVE_FORMAT_PCM );       /* 0x0001 */
        !           712:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.channels , 2 );
        !           713:                Avi_StoreU32 ( pAviFileHeader->AudioStream.Format.sample_rate , AudioFreq );
        !           714:                Avi_StoreU32 ( pAviFileHeader->AudioStream.Format.bit_rate , AudioFreq * 2 * 2 );       /* 2 channels * 2 bytes */
        !           715:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.block_align , 4 );
        !           716:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.bits_per_sample , 16 );
        !           717:                Avi_StoreU16 ( pAviFileHeader->AudioStream.Format.ext_size , 0 );
        !           718:        }
        !           719: }
        !           720: 
        !           721: 
        !           722: static bool    Avi_BuildIndex ( RECORD_AVI_PARAMS *pAviParams )
        !           723: {
        !           724:        AVI_CHUNK       Chunk;
        !           725:        long            IndexChunkPosStart;
        !           726:        long            Pos , PosWrite;
        !           727:        Uint8           TempSize[4];
        !           728:        AVI_CHUNK_INDEX ChunkIndex;
        !           729:        Uint32          Size;
        !           730: 
        !           731:        fseek ( pAviParams->FileOut , 0 , SEEK_END );                           /* go to the end of the file */
        !           732: 
        !           733:        /* Write the 'idx1' chunk header */
        !           734:        IndexChunkPosStart = ftell ( pAviParams->FileOut );
        !           735:        Avi_Store4cc ( Chunk.ChunkName , "idx1" );                              /* stream 0, uncompressed DIB bytes */
        !           736:        Avi_StoreU32 ( Chunk.ChunkSize , 0 );                                   /* index size (-> completed later) */
        !           737:        if ( fwrite ( &Chunk , sizeof ( Chunk ) , 1 , pAviParams->FileOut ) != 1 )
        !           738:         goto index_error;
        !           739:        PosWrite = ftell ( pAviParams->FileOut );                               /* position to start writing indexes */
        !           740: 
        !           741:        /* Go to the first data chunk in the 'movi' chunk */
        !           742:        fseek ( pAviParams->FileOut , pAviParams->MoviChunkPosStart + sizeof ( AVI_STREAM_LIST_MOVI ) , SEEK_SET );
        !           743:        Pos = ftell ( pAviParams->FileOut );
        !           744: 
        !           745:        /* Build the index : we seek/read one data chunk and seek/write the */
        !           746:        /* corresponding infos at the end of the index, until we reach the */
        !           747:        /* end of the 'movi' chunk. */
        !           748:        while ( Pos < pAviParams->MoviChunkPosEnd )
        !           749:        {
        !           750:                /* Read the header for this data chunk: ChunkName and ChunkSize */
        !           751:                if (fread(&Chunk, sizeof(Chunk), 1, pAviParams->FileOut) != 1)
        !           752:             goto index_error;
        !           753:                Size = Avi_ReadU32 ( Chunk.ChunkSize );
        !           754: 
        !           755:                /* Write the index infos for this chunk */
        !           756:                fseek ( pAviParams->FileOut , PosWrite , SEEK_SET );
        !           757:                Avi_Store4cc ( ChunkIndex.identifier , (char *)Chunk.ChunkName );       /* 00dc, 00db, 01wb, ... */
        !           758:                Avi_StoreU32 ( ChunkIndex.flags , AVIIF_KEYFRAME );             /* AVIIF_KEYFRAME */
        !           759:                Avi_StoreU32 ( ChunkIndex.offset , Pos - pAviParams->MoviChunkPosStart - 8  );  /* pos relative to 'movi' */
        !           760:                Avi_StoreU32 ( ChunkIndex.length , Size );
        !           761:                if (fwrite ( &ChunkIndex , sizeof ( ChunkIndex ) , 1 , pAviParams->FileOut ) != 1)
        !           762:             goto index_error;
        !           763:                PosWrite = ftell ( pAviParams->FileOut );                       /* position for the next index */
        !           764: 
        !           765:                /* Go to the next data chunk in the 'movi' chunk */
        !           766:                Pos = Pos + sizeof ( Chunk ) + Size;                            /* position of the next data chunk */
        !           767:                fseek ( pAviParams->FileOut , Pos , SEEK_SET );
        !           768:        }
        !           769: 
        !           770:        /* Update the size of the 'idx1' chunk */
        !           771:        Avi_StoreU32 ( TempSize , PosWrite - IndexChunkPosStart - 8 );
        !           772:        if ( fseek ( pAviParams->FileOut , IndexChunkPosStart+4 , SEEK_SET ) != 0 )
        !           773:         goto index_error;
        !           774:        if ( fwrite ( TempSize , sizeof ( TempSize ) , 1 , pAviParams->FileOut ) != 1 )
        !           775:         goto index_error;
        !           776:        return true;
        !           777: 
        !           778: index_error:
        !           779:     perror ( "Avi_BuildIndex" );
        !           780:     Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to create index header" );
        !           781:     return false;
        !           782: 
        !           783: }
        !           784: 
        !           785: 
        !           786: static bool    Avi_StartRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams , char *AviFileName )
        !           787: {
        !           788:        AVI_STREAM_LIST_INFO    ListInfo;
        !           789:        char                    InfoString[ 100 ];
        !           790:        int                     Len , Len_rounded;
        !           791:        AVI_STREAM_LIST_MOVI    ListMovi;
        !           792: 
        !           793: 
        !           794:        if ( bRecordingAvi == true )                                            /* already recording ? */
        !           795:                return false;
        !           796: 
        !           797:        /* Compute some video parameters */
        !           798:        pAviParams->Width = pAviParams->Surface->w - pAviParams->CropLeft - pAviParams->CropRight;
        !           799:        pAviParams->Height = pAviParams->Surface->h - pAviParams->CropTop - pAviParams->CropBottom;
        !           800:        pAviParams->BitCount = 24;
        !           801:        
        !           802: #if !HAVE_LIBPNG
        !           803:        if ( pAviParams->VideoCodec == AVI_RECORD_VIDEO_CODEC_PNG )
        !           804:        {
        !           805:                perror ( "AviStartRecording" );
        !           806:                Log_AlertDlg ( LOG_ERROR, "AVI recording : Hatari was not built with libpng support" );
        !           807:                return false;
        !           808:        }
        !           809: #endif
        !           810: 
        !           811:        /* Open the file */
        !           812:        pAviParams->FileOut = fopen ( AviFileName , "wb+" );
        !           813:        if ( !pAviParams->FileOut )
        !           814:        {
        !           815:                perror ( "AviStartRecording" );
        !           816:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to open file" );
        !           817:                return false;
        !           818:        }
        !           819: 
        !           820:        /* Build the AVI header */
        !           821:        Avi_BuildFileHeader ( pAviParams , &AviFileHeader );
        !           822:        
        !           823:        /* Write the AVI header */
        !           824:        if ( fwrite ( &AviFileHeader , sizeof ( AviFileHeader ) , 1 , pAviParams->FileOut ) != 1 )
        !           825:        {
        !           826:                perror ( "AviStartRecording" );
        !           827:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write avi header" );
        !           828:                return false;
        !           829:        }
        !           830: 
        !           831:        /* Write the INFO header */
        !           832:        memset ( InfoString , 0 , sizeof ( InfoString ) );
        !           833:        Len = snprintf ( InfoString , sizeof ( InfoString ) , "%s - the Atari ST, STE, TT and Falcon emulator" , PROG_NAME ) + 1;
        !           834:        Len_rounded = Len + ( Len % 2 == 0 ? 0 : 1 );                           /* round Len to the next multiple of 2 */
        !           835:        Avi_Store4cc ( ListInfo.ChunkName , "LIST" );
        !           836:        Avi_StoreU32 ( ListInfo.ChunkSize , sizeof ( AVI_STREAM_LIST_INFO ) - 8 + Len_rounded );
        !           837:        Avi_Store4cc ( ListInfo.Name , "INFO" );
        !           838:        Avi_Store4cc ( ListInfo.Info.ChunkName , "ISFT" );
        !           839:        Avi_StoreU32 ( ListInfo.Info.ChunkSize , Len );
        !           840:        if ( fwrite ( &ListInfo , sizeof ( ListInfo ) , 1 , pAviParams->FileOut ) != 1 )
        !           841:        {
        !           842:                perror ( "AviStartRecording" );
        !           843:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write info header" );
        !           844:                return false;
        !           845:        }
        !           846:        /* Write the info string + '\0' and write an optionnal extra '\0' byte to get a total multiple of 2 */
        !           847:        if ( fwrite ( InfoString , Len_rounded , 1 , pAviParams->FileOut ) != 1 )
        !           848:        {
        !           849:                perror ( "AviStartRecording" );
        !           850:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write info header" );
        !           851:                return false;
        !           852:        }
        !           853: 
        !           854:        /* Write the MOVI header */
        !           855:        Avi_Store4cc ( ListMovi.ChunkName , "LIST" );
        !           856:        Avi_StoreU32 ( ListMovi.ChunkSize , 0 );                                        /* completed when recording stops */
        !           857:        Avi_Store4cc ( ListMovi.Name , "movi" );
        !           858:        pAviParams->MoviChunkPosStart = ftell ( pAviParams->FileOut );
        !           859:        if ( fwrite ( &ListMovi , sizeof ( ListMovi ) , 1 , pAviParams->FileOut ) != 1 )
        !           860:        {
        !           861:                perror ( "AviStartRecording" );
        !           862:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to write movi header" );
        !           863:                return false;
        !           864:        }
        !           865: 
        !           866: 
        !           867:        /* We're ok to record */
        !           868:        Log_AlertDlg ( LOG_INFO, "AVI recording has been started");
        !           869:        bRecordingAvi = true;
        !           870: 
        !           871:        return true;
        !           872: }
        !           873: 
        !           874: 
        !           875: 
        !           876: static bool    Avi_StopRecording_WithParams ( RECORD_AVI_PARAMS *pAviParams )
        !           877: {
        !           878:        long    FileSize;
        !           879:        Uint8   TempSize[4];
        !           880: 
        !           881: 
        !           882:        if ( bRecordingAvi == false )                                           /* no recording ? */
        !           883:                return true;
        !           884: 
        !           885:        /* Update the size of the 'movi' chunk */
        !           886:        fseek ( pAviParams->FileOut , 0 , SEEK_END );                           /* go to the end of the 'movi' chunk */
        !           887:        pAviParams->MoviChunkPosEnd = ftell ( pAviParams->FileOut );
        !           888:        Avi_StoreU32 ( TempSize , pAviParams->MoviChunkPosEnd - pAviParams->MoviChunkPosStart - 8 );
        !           889: 
        !           890:        if ( fseek ( pAviParams->FileOut , pAviParams->MoviChunkPosStart+4 , SEEK_SET ) != 0 )
        !           891:        {
        !           892:                perror ( "AviStopRecording" );
        !           893:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update movi header" );
        !           894:                return false;
        !           895:        }
        !           896:        if ( fwrite ( TempSize , sizeof ( TempSize ) , 1 , pAviParams->FileOut ) != 1 )
        !           897:        {
        !           898:                perror ( "AviStopRecording" );
        !           899:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update movi header" );
        !           900:                return false;
        !           901:        }
        !           902: 
        !           903:        /* Build the index chunk */
        !           904:        if ( ! Avi_BuildIndex ( pAviParams ) )
        !           905:        {
        !           906:                perror ( "AviStopRecording" );
        !           907:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to build index" );
        !           908:                return false;
        !           909:        }
        !           910:        
        !           911:        /* Update the avi header (file size, number of output frames, ...) */
        !           912:        fseek ( pAviParams->FileOut , 0 , SEEK_END );                           /* go to the end of the file */
        !           913:        FileSize = ftell ( pAviParams->FileOut );
        !           914: 
        !           915:        Avi_StoreU32 ( AviFileHeader.RiffHeader.filesize , FileSize - 8 );      /* 32 bits, limited to 4GB */
        !           916:        Avi_StoreU32 ( AviFileHeader.AviHeader.Header.total_frames , pAviParams->TotalVideoFrames );    /* number of video frames */
        !           917:        Avi_StoreU32 ( AviFileHeader.VideoStream.Header.data_length , pAviParams->TotalVideoFrames );   /* number of video frames */
        !           918:        Avi_StoreU32 ( AviFileHeader.AudioStream.Header.data_length , pAviParams->TotalAudioSamples );  /* number of audio samples */
        !           919: 
        !           920:        if ( fseek ( pAviParams->FileOut , 0 , SEEK_SET ) != 0 )
        !           921:        {
        !           922:                perror ( "AviStopRecording" );
        !           923:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update avi header" );
        !           924:                return false;
        !           925:        }
        !           926:        if ( fwrite ( &AviFileHeader , sizeof ( AviFileHeader ) , 1 , pAviParams->FileOut ) != 1 )
        !           927:        {
        !           928:                perror ( "AviStopRecording" );
        !           929:                Log_AlertDlg ( LOG_ERROR, "AVI recording : failed to update avi header" );
        !           930:                return false;
        !           931:        }
        !           932: 
        !           933: 
        !           934:        /* Close the file */
        !           935:        fclose ( pAviParams->FileOut );
        !           936: 
        !           937:        Log_AlertDlg ( LOG_INFO, "AVI recording has been stopped");
        !           938:        bRecordingAvi = false;
        !           939: 
        !           940:        return true;
        !           941: }
        !           942: 
        !           943: 
        !           944: 
        !           945: 
        !           946: /*-----------------------------------------------------------------------*/
        !           947: /**
        !           948:  * Are we recording an AVI ?
        !           949:  */
        !           950: bool   Avi_AreWeRecording ( void )
        !           951: {
        !           952:         return bRecordingAvi;
        !           953: }
        !           954: 
        !           955: 
        !           956: 
        !           957: bool   Avi_StartRecording ( char *FileName , bool CropGui , Uint32 Fps , Uint32 Fps_scale , int VideoCodec )
        !           958: {
        !           959:        memset ( &AviParams , 0 , sizeof ( AviParams ) );
        !           960: 
        !           961:        AviParams.VideoCodec = VideoCodec;
        !           962:        AviParams.VideoCodecCompressionLevel = 9;       /* png compression level */
        !           963:        AviParams.AudioCodec = AVI_RECORD_AUDIO_CODEC_PCM;
        !           964:        AviParams.AudioFreq = ConfigureParams.Sound.nPlaybackFreq;
        !           965:        AviParams.Surface = sdlscrn;
        !           966: 
        !           967:     /* Some video players (quicktime, ...) don't support a value of Fps_scale */
        !           968:     /* above 100000. So we decrease the precision from << 24 to << 16 for Fps and Fps_scale */
        !           969:     AviParams.Fps = Fps >> 8;                  /* refresh rate << 16 */
        !           970:     AviParams.Fps_scale = Fps_scale >> 8;              /* 1 << 16 */
        !           971: 
        !           972:        if ( !CropGui )                                 /* Keep gui's status bar */
        !           973:        {
        !           974:                AviParams.CropLeft = 0;
        !           975:                AviParams.CropRight = 0;
        !           976:                AviParams.CropTop = 0;
        !           977:                AviParams.CropBottom = 0;
        !           978:        }
        !           979:        else                                            /* Record only the content of the Atari's screen */
        !           980:        {
        !           981:                AviParams.CropLeft = 0;
        !           982:                AviParams.CropRight = 0;
        !           983:                AviParams.CropTop = 0;
        !           984:                AviParams.CropBottom = Statusbar_GetHeight();
        !           985:        }
        !           986:        
        !           987:        if (Avi_StartRecording_WithParams ( &AviParams , FileName ))
        !           988:     {
        !           989:         Main_SetTitle("00:00");
        !           990:         return true;
        !           991:     }
        !           992:     return false;
        !           993: }
        !           994: 
        !           995: 
        !           996: bool   Avi_StopRecording ( void )
        !           997: {
        !           998:     if (Avi_StopRecording_WithParams ( &AviParams ))
        !           999:     {
        !          1000:         Main_SetTitle(NULL);
        !          1001:         return true;
        !          1002:     }
        !          1003:     return false;
        !          1004: }
        !          1005: 
        !          1006: 

unix.superglobalmegacorp.com

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