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

1.1       root        1: /*
                      2:   Hatari - avi_record.c
                      3: 
                      4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
                      6: 
                      7:   AVI File recording
                      8: 
                      9:   This allows Hatari to record a video file, with both video and audio
                     10:   streams, at full frame rate.
                     11:   
                     12:   Video frames are saved using the current video frequency of the emulated
                     13:   machine (50 Hz, 60 Hz, 70 Hz, ...). Frames can be stored using different
                     14:   codecs. So far, supported codecs are :
                     15:    - BMP : uncompressed RGB images. Very fast to save, very few cpu needed
                     16:      but requires a lot of disk bandwidth and a lot of space.
                     17:    - PNG : compressed RBG images. Depending on the compression level, this
                     18:      can require more cpu and could slow down Hatari. As compressed images
                     19:      are much smaller than BMP images, this will require less space on disk
                     20:      and much less disk bandwidth. Compression levels 3 or 4 give 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.