#ifndef AVI_H #define AVI_H //#include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #ifdef __cplusplus extern "C" { #endif void bzero(void *s, int n); typedef unsigned char u8; typedef unsigned short u16; typedef unsigned u32; // function prototypes __declspec(dllexport) void __stdcall avi_start(FILE * fp/*, int frame*/); __declspec(dllexport) void __stdcall avi_add(FILE * fp, u8 *buf, int size); __declspec(dllexport) void __stdcall avi_end(FILE * fp, int width, int height, int fps); void fprint_quartet(FILE * fp, unsigned int i); // the following structures are ordered as they appear in a typical AVI struct riff_head { char riff[4]; // chunk type = "RIFF" u32 size; // chunk size char avistr[4]; // avi magic = "AVI " }; // the avih chunk contains a number of list chunks struct avi_head { char avih[4]; // chunk type = "avih" u32 size; // chunk size u32 time; // microsec per frame == 1e6 / fps u32 maxbytespersec; // = 1e6*(total size/frames)/per_usec) u32 pad; // pad = 0 u32 flags; // e.g. AVIF_HASINDEX u32 nframes; // total number of frames u32 initialframes; // = 0 u32 numstreams; // = 1 for now (later = 2 because of audio) u32 suggested_bufsize; // = 0 (no suggestion) u32 width; // width u32 height; // height u32 reserved[4]; // reserved for future use = 0 }; // the LIST chunk contains a number (==#numstreams) of stream chunks struct list_head { char list[4]; // chunk type = "LIST" u32 size; char type[4]; }; struct dmlh_head { char dmlh[4]; // chunk type dmlh u32 size; // 4 u32 nframes; // number of frames }; struct stream_head { char strh[4]; // chunk type = "strh" u32 size; // chunk size char vids[4]; // stream type = "vids" char codec[4]; // codec name (for us, = "MJPG") u32 flags; // contains AVIT_F* flags u16 priority; // = 0 u16 language; // = 0 u32 initialframes; // = 0 u32 scale; // = usec per frame u32 rate; // 1e6 u32 start; // = 0 u32 length; // number of frames u32 suggested_bufsize; // = 0 u32 quality; // = 0 ? u32 samplesize; // = 0 ? }; struct db_head { char db[4]; // "00db" u32 size; }; // a frame chunk contains one JPEG image struct frame_head { char strf[4]; // chunk type = "strf" u32 size; // sizeof chunk (big endian) ? u32 size2; // sizeof chunk (little endian) ? u32 width; u32 height; u16 planes; // 1 bitplane u16 bitcount; // 24 bpl char codec[4]; // MJPG (for us) u32 unpackedsize; // = 3*w*h u32 r1; // reserved u32 r2; // reserved u32 clr_used; // reserved u32 clr_important; // reserved }; struct idx1_head { char idx1[4]; // chunk type = "idx1" u32 size; // chunk size }; #ifdef __cplusplus } #endif #endif
直接上代码
#include "stdafx.h" #include "avi.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #include <vector> using namespace std; // header flags const u32 AVIF_HASINDEX = 0x00000010; /* index at end of file */ const u32 AVIF_MUSTUSEINDEX=0x00000020; const u32 AVIF_ISINTERLEAVED=0x00000100; const u32 AVIF_TRUSTCKTYPE=0x00000800; const u32 AVIF_WASCAPTUREFILE=0x00010000; const u32 AVIF_COPYRIGHTED=0x00020000; int nframes; int totalsize; unsigned int* sizes; vector<unsigned int>jpegSize; void bzero(void *s, int n) { memset(s,0,n); } void fprint_quartet(FILE * fp, unsigned int i) { char data[4]; int rt = 0; data[0] = (char) i%0x100; i /= 0x100; data[1] = (char) i%0x100; i /= 0x100; data[2] = (char) i%0x100; i /= 0x100; data[3] = (char) i%0x100; /*write( fd, &data, 4 );*/ rt = fwrite(&data, 4, 1, fp); if(rt != 1) { printf(" fprintf_quartet failed!\n "); } } // start writing an AVI file void __stdcall avi_start(FILE * fp/*, int frames*/) { int ofs = sizeof(struct riff_head)+ sizeof(struct list_head)+ sizeof(struct avi_head)+ sizeof(struct list_head)+ sizeof(struct stream_head)+ sizeof(struct frame_head)+ sizeof(struct list_head)+ sizeof(struct dmlh_head)+ sizeof(struct list_head); // printf( "avi_start: frames = %d\n", frames ); printf( "avi_start: ofs = %d\n", ofs ); /* lseek(fd, ofs, SEEK_SET);*/ fseek(fp,ofs, SEEK_SET); nframes = 0; totalsize = 0; //sizes = (unsigned int*) calloc( frames, sizeof(unsigned int) ); // hold size of each frame jpegSize.clear(); } // add a jpeg frame to an AVI file void __stdcall avi_add(FILE * fp, u8 *buf, int size) { struct db_head db = {{'0','0','d','b'}, 0}; printf( "avi_add: nframes = %d, totalsize = %d, size = %d\n", nframes, totalsize, size ); db.size = size; /*write( fd, &db, sizeof(db) );*/ fwrite( &db, sizeof(db),1, fp); /*write( fd, buf, size );*/ fwrite( buf, size,1, fp); //sizes[nframes] = size; jpegSize.push_back(size); nframes++; totalsize += size; // total frame size } // finish writing the AVI file - filling in the header void __stdcall avi_end(FILE * fp, int width, int height, int fps) { struct idx1_head idx = {{'i','d','x','1'}, 16*nframes }; struct db_head db = {{'0','0','d','b'}, 0}; //AVI 是空格还是0 struct riff_head rh = { {'R','I','F','F'}, 0, {'A','V','I',' '}}; struct list_head lh1 = {{'L','I','S','T'}, 0, {'h','d','r','l'}}; struct avi_head ah; struct list_head lh2 = {{'L','I','S','T'}, 0, {'s','t','r','l'}}; struct stream_head sh; struct frame_head fh; struct list_head lh3 = {{'L','I','S','T'}, 0, {'o','d','m','l'} }; struct dmlh_head dh = {{'d','m','l','h'}, 4, nframes }; struct list_head lh4 = {{'L','I','S','T'}, 0, {'m','o','v','i'}}; int i; unsigned int offset = 4; printf( "avi_end: nframes = %d, fps = %d/n", nframes, fps ); // write index /*write(fd, &idx, sizeof(idx));*/ fwrite(&idx, sizeof(idx), 1, fp); for ( i = 0; i < nframes; i++ ) { //write(fd, &db, 4 ); // only need the 00db fwrite(&db, 4, 1, fp); fprint_quartet( fp, 18 ); // fprint_quartet( fp, offset ); //fprint_quartet( fp, sizes[i] ); fprint_quartet(fp,jpegSize[i]); //offset += sizes[i] + 8; //+8 (for the additional header) offset += jpegSize[i] + 8; //+8 (for the additional header) } //free( sizes ); jpegSize.clear(); bzero( &ah, sizeof(ah) ); strcpy(ah.avih, "avih"); ah.time = 1000000 / fps; ah.maxbytespersec = 1000000.0*(totalsize/nframes)/ah.time; ah.nframes = nframes; ah.numstreams = 1; ah.flags = AVIF_HASINDEX; ah.width = width; ah.height = height; bzero(&sh, sizeof(sh)); strcpy(sh.strh, "strh"); strcpy(sh.vids, "vids"); strcpy(sh.codec, "MJPG"); sh.scale = ah.time; sh.rate = 1000000; sh.length = nframes; bzero(&fh, sizeof(fh)); strcpy(fh.strf, "strf"); fh.width = width; fh.height = height; fh.planes = 1; fh.bitcount = 24; strcpy(fh.codec,"MJPG"); fh.unpackedsize = 3*width*height; rh.size = sizeof(lh1)+sizeof(ah)+sizeof(lh2)+sizeof(sh)+ sizeof(fh)+sizeof(lh3)+sizeof(dh)+sizeof(lh4)+ nframes*sizeof(struct db_head)+ totalsize + sizeof(struct idx1_head)+ (16*nframes) +4; // FIXME:16 bytes per nframe // the '4' - what for??? lh1.size = 4+sizeof(ah)+sizeof(lh2)+sizeof(sh)+sizeof(fh)+sizeof(lh3)+sizeof(dh); ah.size = sizeof(ah)-8; lh2.size = 4+sizeof(sh)+sizeof(fh)+sizeof(lh3)+sizeof(dh); //4+sizeof(sh)+sizeof(fh); sh.size = sizeof(sh)-8; fh.size = sizeof(fh)-8; fh.size2 = fh.size; lh3.size = 4+sizeof(dh); lh4.size = 4+ nframes*sizeof(struct db_head)+ totalsize; fseek(fp,0, SEEK_SET); fwrite( &rh, sizeof(rh), 1, fp); fwrite( &lh1, sizeof(lh1), 1, fp); fwrite( &ah, sizeof(ah), 1, fp); fwrite( &lh2, sizeof(lh2), 1, fp); fwrite( &sh, sizeof(sh), 1, fp); fwrite( &fh, sizeof(fh), 1, fp); fwrite(&lh3, sizeof(lh3), 1, fp); fwrite(&dh, sizeof(dh), 1, fp); fwrite(&lh4, sizeof(lh4), 1, fp); }
调用例子:
// testJpegToAvi.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "windows.h" #include <vector> #include <string> #include <algorithm> using namespace std; #include "../JpegToAvi/Avi.h" bool ReadJpeg(const std::string JpegPath,unsigned char *pJpegBuff,unsigned int &JpegLen) { if (pJpegBuff==NULL) { return false; } FILE *fp = NULL; errno_t err; if ((err = fopen_s(&fp, JpegPath.c_str(), "rb")) != 0) { printf("open file for write error\n"); if (fp!=NULL) { fclose(fp); fp=NULL; } return false; } fseek(fp, 0, SEEK_END); unsigned int len = ftell(fp); fseek(fp, 0, SEEK_SET); fread(pJpegBuff,len,1,fp); JpegLen=len; if (fp!=NULL) { fclose(fp); fp=NULL; } return true; } static bool CmpFileName(const string& a, const string& b) { return a.length() == b.length() ? a < b : a.length() < b.length(); } // //枚举Jpeg图片 void EnumerateImageFile( string imgPath, vector<string>& fileNames, bool bRecursive = false ) { fileNames.clear(); WIN32_FIND_DATA findData; string path = imgPath; if ( path[path.length()-1] != '\\' ) path += "\\"; path += "*.*"; HANDLE hFirstFile = ::FindFirstFile( path.c_str(), &findData ); if( hFirstFile != INVALID_HANDLE_VALUE ) { do { if( (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ) { if( bRecursive && strcmp( findData.cFileName, "." ) != 0 && strcmp( findData.cFileName, ".." ) != 0 ) EnumerateImageFile( (imgPath+"\\"+findData.cFileName).c_str(), fileNames, bRecursive ); } else { string fileName( findData.cFileName ); int pos = fileName.find_last_of( '.' ); if( pos != -1 ) { string ext = fileName.substr(pos+1, fileName.length()-pos); /// 寻找jpeg文件 if( ext == "jpg" || ext == "JPG" || ext == "jpeg" || ext == "JPEG") { fileNames.push_back( imgPath + findData.cFileName ); } } } }while( FindNextFile( hFirstFile, &findData ) ); ::FindClose( hFirstFile ); } std::sort(fileNames.begin(), fileNames.end(), CmpFileName); } // int _tmain(int argc, _TCHAR* argv[]) { vector<string> m_vecFilePic; //jpeg目录 int m_nFilePicID=0; //jpeg图片序列第几帧 std::string imgPath="../jpeg/"; EnumerateImageFile(imgPath, m_vecFilePic); FILE *Fp = NULL; errno_t err; if ((err = fopen_s(&Fp, "../jpeg/1.avi", "wb")) != 0) { printf("open file for write error\n"); if (Fp!=NULL) { fclose(Fp); Fp=NULL; } return -1; } avi_start(Fp); unsigned char *pBuff=new unsigned char [1024*1024]; memset(pBuff,0,1024*1024); unsigned int len=0; for(unsigned int i=0;i<m_vecFilePic.size();i++) { // ReadJpeg(m_vecFilePic[i].c_str(),pBuff,len); avi_add(Fp,pBuff,len); } avi_end(Fp,1920,1080,25); if (Fp!=NULL) { fclose(Fp); Fp=NULL; } return 0; }