#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;
}