2020年10月1日 星期四

用 c++ 將 jpeg 檔封裝成 avi 格式檔

    #include <stdio.h>
    #include <string.h> 
    #include <stdlib.h> 
    #include <unistd.h>
   
    struct RCwindow    {
        unsigned short int left;
        unsigned short int top;
        unsigned short int right;
        unsigned short int bottom;     
    };

    struct AviChunkHdr  {
        unsigned char fourcc[4];
        unsigned int  dataSize;
        // ... data
    };// Chunk header, dataSize = sizeof(whole structure) - 8

    struct AviListHdr  {
        unsigned char fourcc[4];
        unsigned int  dataSize;
        unsigned char listtype[4];
        // ... list
    };// List header, dataSize = sizeof(whole structure) - 8

    struct AviStrf {
        unsigned char fourcc[4];
        unsigned int dataSize;

        unsigned int strucSize;
        unsigned int width;
        unsigned int height;
        unsigned int bitsCount;
        unsigned char compression[4];
        unsigned int imgSize;
        unsigned int xpelsMeter;
        unsigned int ypelsMeter;
        unsigned int numColors;
        unsigned int impColors;
    };// Chunk: avi stream format

    struct AviHeader   {
        unsigned char fourcc[4];
        unsigned int dataSize;

        unsigned int usFrame;
        unsigned int bytesPS;
        unsigned int padding;
        unsigned int flags;
        unsigned int totalFrames;
        unsigned int initFrame;
        unsigned int streams;
        unsigned int bufferSize;
        unsigned int width;
        unsigned int height;
        unsigned int reserved[4];
    };// Chunk: avi header
   
    struct AviListStrh   {
        unsigned char fourcc[4];
        unsigned int  dataSize;
        unsigned char listtype[4];

        unsigned char stype[4];
        unsigned int flags;
        unsigned int priority;
        unsigned int start;
        unsigned int scale;
        unsigned int rate;
        unsigned int initFrame;
        unsigned int totalFrames;
        unsigned int bufferSize;
        unsigned int quality;
        unsigned int samples;
        RCwindow rectangle;
    };// List: stream header


    struct AviListOdml {
        unsigned char fourcc[4];
        unsigned int  dataSize;
        unsigned char listtype[4];

        unsigned char stype[4];
        unsigned int  intSize;
        unsigned int  jpgFrames;
    };// List: openDML

    struct AviListStrl {
        unsigned char fourcc[4];
        unsigned int  dataSize;
        unsigned char listtype[4];

        AviListStrh strh;
        AviStrf strf;
        AviListOdml list_odml;
    };//List: stream list

    struct AviListHdrlist  {
        unsigned char fourcc[4];
        unsigned int  dataSize;
        unsigned char listtype[4];

        AviHeader avih;
        AviListStrl strl;
    };// List: aviheader + streamlist

    struct AviRiff {
        AviListHdr  riffhdr;
        AviListHdrlist headerList;
        AviListHdr  movieList;
    };// Lists: RIFF avi Tag + headerList + movieList
   
    struct Jpeg {
        unsigned short int soi;
        unsigned short int app0;
        unsigned short int hlen;
        char id[5];
    };

    int main(int argc, char **argv)  {
        unsigned int width  = 0; 
        unsigned int height = 0;
        sscanf(argv[argc - 1], "%ux%u", &width, &height);
        unsigned short jpgFrames = argc - 1;
        if (width > 1 && height > 1) jpgFrames --;       
        else { // default size
            width  = 800;
            height = 600;
        }
        if(jpgFrames == 0) {
            printf("usage:\n\t%s %s\n", argv[0], "1.jpg [...] [widthxheight]");
            exit(1);
        }
       
        unsigned int aviSize = 0;// max 2^32 = 4G       
        int bufferSize = 1024;
        char buffer[bufferSize + 1];
        char aviName[] = "test.avi";
        FILE *avi = fopen(aviName, "wb");
        if(avi == nullptr) {           
            printf("create avi file fail!\n");
            exit(1);
        }

        int fps = 1;// frame per second
        unsigned int usScale = 1e6;
        unsigned int sFrame = usScale/fps;

        AviListOdml odml = {           
            {'L', 'I', 'S', 'T'}, sizeof(AviListOdml) - 8,
            {'o', 'd', 'm', 'l'}, {'d', 'm', 'l', 'h'},
            sizeof(unsigned int), jpgFrames  
        };// open dml extention

        AviStrf strf = {
            {'s', 't', 'r', 'f'}, sizeof(AviStrf) - 8,
            sizeof(AviStrf),  
            width, height, 1 + (24 << 16),
            {'M', 'J', 'P', 'G'},  
            width * height * 3,  
            0,  0,  0,  0
        };// stream format, video
       
        AviListStrh strh = {
            {'s', 't', 'r', 'h'}, sizeof(AviListStrh) - 8,
            {'v', 'i', 'd', 's'}, {'m', 'j', 'p', 'g'},
            0, 0, 0,
            sFrame, usScale, 0, jpgFrames,  
            0, 0, 0,
            {0, 0, (unsigned short int)width, (unsigned short int)height}
        };// stream header       

        AviListStrl streamlist = {              
            {'L', 'I', 'S', 'T'}, sizeof(AviListStrl) - 8,
            {'s', 't', 'r', 'l'}, strh, strf, odml
        };// stream list

        AviHeader avih  = {
            {'a', 'v', 'i', 'h'}, sizeof(AviHeader) - 8,
            sFrame, fps * aviSize/jpgFrames, 0, 0, jpgFrames,  
            0, 1, 0, width, height, {0}
        };// avi header without index

        AviRiff riff = {
            {// AviListHdr
                {'R', 'I', 'F', 'F'}, 0,
                {'A', 'V', 'I', ' '}
            },
            {// AviListHdrlist                
                {'L', 'I', 'S', 'T'}, sizeof(AviListHdrlist) - 8,
                {'h', 'd', 'r', 'l'}, avih, streamlist
            },
            { // AviListHdr
                {'L', 'I', 'S', 'T'}, 0,
                {'m', 'o', 'v', 'i'}
            }    
        };

        auto fwritei = [](unsigned int i, FILE *f){
            for(int j = 0; j < 4 ; j ++) {
                fputc(i & 0xff, f);// LSB first out
                i >>= 8;
            }
        };
       
        long totalBytes = 0;
        fseek(avi, sizeof(riff), SEEK_SET);
        for(int k = 0; k < jpgFrames; k ++) {
            int index = k + 1;
            FILE *jpgFile= fopen(argv[index], "rb");
            if (jpgFile == nullptr) continue;

            long flen = fread(buffer, 1, 11, jpgFile);
            buffer[flen] = 0;
            auto &jpeg = * (Jpeg *)buffer;
            if  (flen == 11 && jpeg.soi == 0xd8ff && jpeg.app0 == 0xe0ff &&
                    strcmp(jpeg.id,"JFIF") == 0) {
                fseek(jpgFile, 0L, SEEK_END);
                flen = ftell(jpgFile);
                printf("%04d. %-12s %-12ld bytes\n", index, argv[index], flen);

                fwrite("00dc", 4, 1, avi);
                fwritei(flen, avi);
                fwrite(buffer, 6, 1, avi);
                fwrite("AVI1", 5, 1, avi);
                fseek(jpgFile, 11L, SEEK_SET);
                int n = 0;
                do {
                    n = fread(buffer, 1, bufferSize, jpgFile);
                    if(n > 0) fwrite(buffer, n, 1, avi);
                } while(n == bufferSize);
                totalBytes += flen;
                if(flen % 2) {
                    flen ++;
                    fputc(0, avi);
                }
                aviSize += flen + 8;
            }
            fclose(jpgFile);
        }
       
        riff.movieList.dataSize  = 4 + aviSize;
        riff.riffhdr.dataSize = riff.movieList.dataSize  + sizeof(AviRiff) - 8;
        riff.headerList.avih.bytesPS = fps * totalBytes / jpgFrames;
       
        fseek(avi, 0l, SEEK_SET);
        fwrite(&riff, sizeof(riff), 1, avi);

        printf("\nmovieSize=%d, riffSize=%d, maxBytesPS=%d\n",
            riff.movieList.dataSize,
            riff.riffhdr.dataSize,
            riff.headerList.avih.bytesPS
        );
        printf("%s %ld/(%d bytes) includes %u frames(%u*%u):\n\n",
            aviName,
            sizeof(riff),
            4 + riff.riffhdr.dataSize,
            jpgFrames,
            width,
            height
        );
        fclose(avi);
    }

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...