Annotation of previous/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 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.