这是笔者写的一个小的类,用于把一系列jpeg文件转换为avi。
首先把源码帖出来:
//AviFormat.h
#ifndef _AVI_FORMAT_H_
#define _AVI_FORMAT_H_
#include <iostream>
using namespace std;
/* 4 bytes */
typedef int WORD;
typedef unsigned int DWORD;
/* for use in AVI_avih.flags */
const DWORD AVIF_HASINDEX = 0x00000010; /* index at end of file */
const DWORD AVIF_MUSTUSEINDEX = 0x00000020;
const DWORD AVIF_ISINTERLEAVED = 0x00000100;
const DWORD AVIF_TRUSTCKTYPE = 0x00000800;
const DWORD AVIF_WASCAPTUREFILE = 0x00010000;
const DWORD AVIF_COPYRIGHTED = 0x00020000;
struct AVI_avih
{
DWORD usec_per_frame; //* frame display rate (or 0L) */
DWORD max_bytes_per_sec; //* max. transfer rate */
DWORD padding; //* pad to multiples of this size; */
/* normally 2K */
DWORD flags;
DWORD tot_frames; //* # frames in file */
DWORD init_frames;
DWORD streams;
DWORD buff_sz;
DWORD width;
DWORD height;
DWORD reserved[4];
AVI_avih () {}
AVI_avih (DWORD usec, DWORD max, DWORD pad, DWORD flags, DWORD tot, DWORD init, DWORD str, DWORD buff, DWORD w, DWORD h, DWORD* re = NULL)
:usec_per_frame (usec), max_bytes_per_sec (max), padding (pad), tot_frames (tot)
,init_frames (init), streams (str), buff_sz (buff), width (w), height (h) {
if (re != NULL)
for (int i = 0; i < 4; ++i)
reserved[i] = re[i];
else
for (int i = 0; i < 4; ++i)
reserved[i] = 0;
}
AVI_avih (AVI_avih const& avih) {
usec_per_frame = avih.usec_per_frame;
max_bytes_per_sec = avih.max_bytes_per_sec;
padding = avih.padding;
flags = avih.flags;
tot_frames = avih.tot_frames;
init_frames = avih.init_frames;
streams = avih.streams;
buff_sz = avih.buff_sz;
width = avih.width;
height = avih.height;
for (int i; i < 4; ++i)
reserved[i] = avih.reserved[i];
}
~AVI_avih (){}
friend ostream& operator << (ostream& out, AVI_avih const& avih) {
out << avih.usec_per_frame << avih.max_bytes_per_sec << avih.padding << avih.flags << avih.tot_frames << avih.init_frames;
out << avih.streams << avih.buff_sz << avih.width << avih.height;
out << avih.reserved[0] << avih.reserved[1] << avih.reserved[2] << avih.reserved[3];
return out;
}
};
struct AVI_strh
{
unsigned char type[4]; ///* stream type */
unsigned char handler[4];
DWORD flags;
DWORD priority;
DWORD init_frames; ///* initial frames (???) */
DWORD scale;
DWORD rate;
DWORD start;
DWORD length;
DWORD buff_sz; ///* suggested buffer size */
DWORD quality;
DWORD sample_sz;
AVI_strh () {}
AVI_strh (char const* t, char const* h, DWORD f, DWORD p, DWORD in, DWORD sc, DWORD r, DWORD st, DWORD l, DWORD bs, DWORD q, DWORD ss)
: flags (f), priority (p), init_frames (in), scale (sc)
, rate (r), start (st), length (l), buff_sz (bs)
, quality (q) ,sample_sz (ss) {
for (int i = 0; i < 4; ++i) {
type[i] = t[i];
handler[i] = h[i];
}
}
AVI_strh (AVI_strh const& strh) {
flags = strh.flags;
priority = strh.priority;
init_frames = strh.init_frames;
scale = strh.scale;
rate = strh.rate;
start = strh.start;
length = strh.length;
buff_sz = strh.buff_sz;
quality = strh.quality;
sample_sz = strh.sample_sz;
for (int i = 0; i < 4; ++i) {
type[i] = strh.type[i];
handler[i] = strh.handler[i];
}
}
~AVI_strh () {}
friend ostream& operator << (ostream& out, AVI_strh const& strh) {
out << strh.type[0] << strh.type[1] << strh.type[2] << strh.type[3];
out << strh.handler[0] << strh.handler[1] << strh.handler[2] << strh.handler[3];
out << strh.flags << strh.priority << strh.init_frames << strh.scale << strh.rate;
out << strh.start << strh.length << strh.buff_sz << strh.quality << strh.sample_sz;
return out;
}
};
struct AVI_strf
{
DWORD sz;
DWORD width;
DWORD height;
DWORD planes_bit_cnt;
unsigned char compression[4];
DWORD image_sz;
DWORD xpels_meter;
DWORD ypels_meter;
DWORD num_colors; // used colors
DWORD imp_colors; // important colors
/*
may be more for some codecs
*/
AVI_strf () {}
AVI_strf (DWORD s, DWORD w, DWORD h, DWORD p, char const* c, DWORD i, DWORD x, DWORD y, DWORD n, DWORD imp)
:sz (s), width (w), height (h), planes_bit_cnt (p), image_sz (i)
,xpels_meter (x), ypels_meter (y), num_colors (n), imp_colors (imp) {
for (int i = 0; i < 4; ++i)
compression[i] = c[i];
}
AVI_strf (AVI_strf const& strf) {
sz = strf.sz;
width = strf.width;
height = strf.height;
planes_bit_cnt = strf.planes_bit_cnt;
image_sz = strf.image_sz;
xpels_meter = strf.xpels_meter;
ypels_meter = strf.ypels_meter;
num_colors = strf.num_colors;
imp_colors = strf.imp_colors;
for (int i = 0; i < 4; ++i)
compression[i] = strf.compression[i];
}
~AVI_strf () {}
friend ostream& operator << (ostream& out, AVI_strf const& strf) {
out << strf.sz << out << strf.width << strf.height << strf.planes_bit_cnt;
out << strf.compression[0] << strf.compression[1] << strf.compression[2] << strf.compression[3];
out << strf.image_sz << strf.xpels_meter << strf.ypels_meter << strf.num_colors << strf.imp_colors;
return out;
}
};
/*
AVI_list_hdr
spc: a very ubiquitous AVI struct, used in list structures
to specify type and length
*/
struct AVI_list_hdr
{
unsigned char id[4]; /* "LIST" */
DWORD sz; /* size of owning struct minus 8 */
unsigned char type[4]; /* type of list */
AVI_list_hdr () {}
AVI_list_hdr (char const* list, DWORD d, char const* value) {
sz = d;
for (int i = 0; i < 4; ++i) {
id[i] = list[i];
type[i] = value[i];
}
}
AVI_list_hdr (AVI_list_hdr const& hdr) {
for (int i = 0; i < 4; ++i) {
id[i] = hdr.id[i];
type[i] = hdr.type[i];
}
sz = hdr.sz;
}
~AVI_list_hdr () {}
friend ostream& operator<< (ostream& out, AVI_list_hdr const& hdr) {
out << hdr.id[0] << hdr.id[1] << hdr.id[3] << hdr.id[4] << hdr.sz << hdr.type[0] << hdr.type[1] << hdr.type[2] << hdr.type[3];
return out;
}
};
struct AVI_list_odml
{
struct AVI_list_hdr list_hdr;
unsigned char id[4];
DWORD sz;
DWORD frames;
AVI_list_odml () {}
AVI_list_odml (char const* l1, DWORD d1, char const* v1, char const* l2, DWORD d2, DWORD f)
:list_hdr (l1, d1, v1), sz (d2), frames (f) {
for (int i = 0; i < 4; ++i)
id[i] = l2[i];
}
AVI_list_odml (AVI_list_hdr const hdr, char const* v, DWORD d2, DWORD f)
: list_hdr (hdr), sz (d2), frames (f) {
for (int i = 0; i < 4; ++i)
id[i] = v[i];
}
AVI_list_odml (AVI_list_odml const& odml)
:list_hdr (odml.list_hdr), sz (odml.sz), frames (odml.frames) {
for (int i = 0; i < 4; ++i)
id[i] = odml.id[i];
}
~AVI_list_odml () {}
friend ostream& operator << (ostream& out, AVI_list_odml const& odml){
out << odml.list_hdr << odml.id[0] << odml.id[1] << odml.id[2] << odml.id[3];
out << odml.sz << odml.frames;
return out;
}
};
struct AVI_list_strl
{
struct AVI_list_hdr list_hdr;
/* chunk strh */
unsigned char strh_id[4];
DWORD strh_sz;
struct AVI_strh strh;
/* chunk strf */
unsigned char strf_id[4];
DWORD strf_sz;
struct AVI_strf strf;
/* list odml */
struct AVI_list_odml list_odml;
};
struct AVI_list_hdrl
{
struct AVI_list_hdr list_hdr;
/* chunk avih */
unsigned char avih_id[4];
DWORD avih_sz;
struct AVI_avih avih;
/* list strl */
struct AVI_list_hdr strl_hdr;
/* chunk strh */
unsigned char strh_id[4];
DWORD strh_sz;
struct AVI_strh strh;
/* chunk strf */
unsigned char strf_id[4];
DWORD strf_sz;
struct AVI_strf strf;
/* list odml */
struct AVI_list_odml list_odml;
AVI_list_hdrl (DWORD width = 0, DWORD height = 0, DWORD jpg_sz = 1, DWORD per_usec = 1, DWORD frames = 1)
:list_hdr ("LIST", sizeof (struct AVI_list_hdrl) - 8, "hdrl")
//* chunk avih */
,avih_sz (sizeof (struct AVI_avih))
,avih (per_usec, 1000000 * (jpg_sz/frames) / per_usec, (0)
,AVIF_HASINDEX, frames, 0, 1, 0, width, height)
// list strl
,strl_hdr ("LIST", sizeof (struct AVI_list_strl) - 8, "strl")
// chunk strh
,strh_sz (sizeof (struct AVI_strh))
,strh ("vids", "MJPG", 0, 0, 0, per_usec, 1000000
, 0, frames, 0, 0, 0)
// chunk strf
,strf_sz (sizeof (AVI_strf))
,strf (sizeof (struct AVI_strf), width, height, 1 + 24*256*256
,"MJPG", width * height * 3, 0, 0, 0, 0)
// list odml
,list_odml ("LIST", 16, "odml", "dmlh", 4, frames) {
avih_id[0] = 'a'; avih_id[1] = 'v'; avih_id[2] = 'i'; avih_id[3] = 'h';
strh_id[0] = 's'; strh_id[1] = 't'; strh_id[2] = 'r'; strh_id[3] = 'h';
strf_id[0] = 's'; strf_id[1] = 't'; strf_id[2] = 'r'; strf_id[3] = 'f';
}
~AVI_list_hdrl (){}
friend ostream& operator << (ostream& out, AVI_list_hdrl const& hdrl) {
out << hdrl.list_hdr << hdrl.avih_id[0] << hdrl.avih_id[1] << hdrl.avih_id[2] << hdrl.avih_id[3];
out << hdrl.avih_sz << hdrl.strl_hdr;
out << hdrl.strh_id[0] << hdrl.strh_id[1] << hdrl.strh_id[2] << hdrl.strh_id[3];
out << hdrl.strh_sz << hdrl.strh;
out << hdrl.strf_id[0] << hdrl.strf_id[1] << hdrl.strf_id[2] << hdrl.strf_id[3];
out << hdrl.strf_sz << hdrl.strf << hdrl.list_odml;
return out;
}
};
#endif //_AVI_FORMAT_H_
//AviGenerator.h
#ifndef _AVI_GENERATOR_H_
#define _AVI_GENERATOR_H_
#define DEBUG_VERSION
#include <stdio.h>
#include <string>
#include <vector>
#include "AviFormat.h"
using namespace std;
/* 4 bytes */
typedef int WORD;
typedef unsigned int DWORD;
/* 1 byte */
typedef unsigned char BYTE;
class AviGenerator
{
public:
AviGenerator(void);
~AviGenerator(void);
void set_avi_file (string const& file);
void set_fps (int fps);
int add_frame (string const& file);
void set_avi_size (int w, int h);
int generate_avi ();
private:
int file_size (string const& file);
int files_size ();
int initalize_header ();
void print_quartet (FILE* file, DWORD i);
public:
static DWORD const MAX_RIFF_SZ = 2147483648LL; /*Max avi file size, 2 GB limit*/
static DWORD const JPEG_DATA_SZ = sizeof(DWORD) * 2;
struct Jpeg_Data
{
DWORD size;
DWORD offset;
string name; /* i.e. variable length structure */
Jpeg_Data () {}
~Jpeg_Data () {}
friend bool operator< (Jpeg_Data const& a, Jpeg_Data const& o) {
return a.name < o.name;
}
static bool lestthan (Jpeg_Data const* a, Jpeg_Data const* o) {
return a->name < o->name;
}
};
protected:
string avi_name_;
WORD fps_;
WORD frames_;
WORD usec_per_frame;
//file structure
AVI_list_hdrl list_hdrl;
//file list
vector <Jpeg_Data*> jpeg_list;
//option
DWORD width;
DWORD height;
DWORD frames;
unsigned int fps;
long jpg_sz;
size_t jpg_sz_64;
size_t riff_sz_64;
DWORD riff_sz;
};
#endif
//AviGenerator.cpp
#include "AviGenerator.h"
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <algorithm>
#ifdef _WIN32
#include <io.h>
#include <errno.h>
#else
#include <sys/param.h>
#include <unistd.h>
#endif
using namespace std;
AviGenerator::AviGenerator(void)
:fps (15) ,frames (1), jpg_sz (1), width (320), height (240)
{
usec_per_frame = 1000000 / fps;
}
AviGenerator::~AviGenerator(void)
{
for (vector<Jpeg_Data*>::iterator iter = this->jpeg_list.begin (); iter != jpeg_list.end (); ++iter) {
Jpeg_Data* p = *iter;
delete p;
}
this->jpeg_list.clear ();
}
void AviGenerator::set_avi_file (string const& file)
{
this->avi_name_ = file;
}
void AviGenerator::set_fps (int fps)
{
this->fps_ = fps;
this->usec_per_frame = 1000000 / fps;
}
void AviGenerator::set_avi_size (int w, int h)
{
this->width = w;
this->height = h;
}
int AviGenerator::file_size (string const& file)
{
int ret;
#ifdef _WIN32
struct __stat64 result;
if (-1 == _stat64 (file.c_str (), &result))
return -1;
ret = result.st_size;
#else
struct stat s;
if (-1 == stat(file.c_str (), &s))
return -1;
ret = s.st_size;
#endif
#ifdef DEBUG_VERSION
cerr << file.c_str () << " size is " << ret << endl;
#endif
return ret;
}
int AviGenerator::files_size ()
{
int ret = 0;
int tmp, it;
for (vector<Jpeg_Data*>::iterator iter = jpeg_list.begin (); iter != jpeg_list.end (); ++iter) {
it = (*iter)->size;
tmp = it != 0?it:file_size ((*iter)->name.c_str ());
ret += tmp;
ret += (4 - (tmp % 4)) % 4;
}
return ret;
}
int AviGenerator::add_frame (string const& file)
{
int size = file_size (file);
if (size <= 0)
return -1;
struct Jpeg_Data* jpeg= new Jpeg_Data ();
jpeg->name = file;
jpeg->size = size;
jpeg->offset = 0;
this->jpeg_list.insert (jpeg_list.begin (), jpeg);
return 0;
}
void AviGenerator::print_quartet (FILE* file, DWORD i)
{
for (int j = 0; j < 4; ++j) {
fputc (i % 0x0100, file);
i /= 0x100;
}
}
int AviGenerator::initalize_header ()
{
frames = this->jpeg_list.size ();
if (frames <= 0)
return -1;
/* getting image, and hence, riff sizes */
jpg_sz_64 = this->files_size ();
if (-1 == jpg_sz_64) {
cerr << "couldn't determine size of images" << endl;
return -2;
}
riff_sz_64 = sizeof (struct AVI_list_hdrl) + 4 + 4 + jpg_sz_64 + 8 * frames + 8 + 8 + 16 * frames;
if (riff_sz_64 >= MAX_RIFF_SZ) {
cerr << "RIFF would exceed 2 Gb limit" << endl;
return -3;
}
jpg_sz = (long) jpg_sz_64;
riff_sz = (DWORD) riff_sz_64;
//update the struct AVI_list_hdrl
this->list_hdrl.avih.usec_per_frame = LILEND4(usec_per_frame);
this->list_hdrl.avih.max_bytes_per_sec = LILEND4((int)1000000 * (jpg_sz / frames) / usec_per_frame);
this->list_hdrl.avih.flags = LILEND4(AVIF_HASINDEX);
this->list_hdrl.avih.tot_frames = LILEND4(frames);
this->list_hdrl.avih.width = LILEND4(width);
this->list_hdrl.avih.height = LILEND4(height);
this->list_hdrl.strh.scale = LILEND4(usec_per_frame);
this->list_hdrl.strh.rate = LILEND4(1000000);
this->list_hdrl.strh.length = LILEND4(frames);
this->list_hdrl.strf.width = LILEND4(width);
this->list_hdrl.strf.height = LILEND4(height);
this->list_hdrl.strf.image_sz = LILEND4(width * height * 3);
this->list_hdrl.list_odml.frames = LILEND4(frames);
return 0;
}
int AviGenerator::generate_avi ()
{
if (this->initalize_header () != 0)
return -1;
//open the file
FILE* fdest = fopen (this->avi_name_.c_str (), "wb");
if (NULL == fdest) {
cerr << "Can't create a new file to write (" << this->avi_name_ << ")!" << endl;
return -2;
}
//fwrite (&list_hdrl.avih.max_bytes_per_sec, 4, 1, fdest);
long nbr;
long nbw;
long tnbw = 0;
long mfsz;
long remnant;
char buff[512];
//write file header
fwrite ("RIFF", 4, 1, fdest);
print_quartet (fdest, riff_sz);
fwrite ("AVI ", 4, 1, fdest);
fwrite (&list_hdrl, sizeof (struct AVI_list_hdrl), 1, fdest);
//sort the list by file name
sort (this->jpeg_list.begin (), this->jpeg_list.end (), AviGenerator::Jpeg_Data::lestthan);
// list movi
size_t offset = 4;
fwrite ("LIST", 4, 1, fdest);
print_quartet (fdest, jpg_sz + 8 * frames + 4);
fwrite ("movi", 4, 1, fdest);
//write video data
for (vector <Jpeg_Data*>::iterator iter = this->jpeg_list.begin (); iter != jpeg_list.end (); ++iter) {
#ifdef DEBUG_VERSION
cout << "dealing with " << (*iter)->name << endl;
#endif
fwrite ("00db", 4, 1, fdest);
mfsz = (*iter)->size;
remnant = (4 - (mfsz % 4)) % 4;
print_quartet (fdest, mfsz + remnant);
(*iter)->size += remnant;
(*iter)->offset = offset;
offset += (*iter)->size + 8;
int fd;
#ifdef _WIN32
fd = open ((*iter)->name.c_str (), O_RDONLY | O_BINARY);
#else
fd = open ((*iter)->name.c_str (), O_RDONLY);
#endif
if (fd < 0) {
cerr << "couldn't open file (" << (*iter)->name << ")!" << endl;
fclose (fdest);
return -3;
}
nbw = 0;
if ((nbr = read (fd, buff, 6)) != 6) {
cerr << "reading error" << endl;
fclose (fdest);
close (fd);
return -4;
}
fwrite (buff, nbr, 1, fdest);
read (fd, buff, 4);
fwrite ("AVI1", 4, 1, fdest);
nbw = 10;
while ((nbr = read (fd, buff, 512)) > 0){
#ifdef DEBUG_VERSION
//cout << "read " << nbr << " bytes from " << (*iter)->name << endl;
#endif
fwrite (buff, nbr, 1, fdest);
nbw += nbr;
}
if (remnant > 0) {
fwrite (buff, remnant, 1, fdest);
nbw += remnant;
}
tnbw += nbw;
close (fd);
}
if (tnbw != jpg_sz) {
cerr << "error writing images (wrote " << tnbw << " bytes, expected " << jpg_sz << " bytes)" << endl;
fclose (fdest);
return -5;
}
/* indices */
fwrite ("idx1", 4, 1, fdest);
print_quartet (fdest, 16 * frames);
for (vector <Jpeg_Data*>::iterator iter = this->jpeg_list.begin (); iter != jpeg_list.end (); ++iter) {
fwrite ("00db", 4, 1, fdest);
print_quartet (fdest, 18);
print_quartet (fdest, (*iter)->offset);
print_quartet (fdest, (*iter)->size);
}
//this->jpeg_list.clear ();
fclose (fdest);
return 0;
}
//main.cpp 测试文件
// tmp.cpp : 定义控制台应用程序的入口点。
//
#include <iostream>
#include <fstream>
#include "AviGenerator.h"
using namespace std;
int main(int argc, char* argv[])
{
//ofstream fout ("temp.txt", ios::out);
//fout.close ();
AviGenerator generator;
for (int i = 1; i <= 38; ++i) {
char c[32];
sprintf (c, "mao%02d", i);//加入jpeg文件,我有mao01~38.jpg文件
strcat (c, ".jpg");
cout << c << endl;
generator.add_frame (c);
}
cout << "generating avi file......" << endl;
if (argc >=2)
generator.set_avi_file (argv[1]);
else
generator.set_avi_file ("mao.avi");
generator.set_fps (15);
generator.set_avi_size (230, 100);
generator.generate_avi ();
//system ("pause");
return 0;
}
Makefile文件就不作了,只有两个.cpp文件,对于大家编译应该没有什么问题吧。
笔者在原有的基础上把原来的C代码改写成了一个类,便于使用(如果不喜欢完全可以去找“原版”的C代码),并加入了Win32平台的支持(其实对于笔者来说没什么用——笔者的程序是在Linux下运行的)。
笔者在VC2003、VC2005 beta1编译,以及用g++、c++都是没有问题的,生成的avi文件用media player正常播放,但是因为没有进行压缩,所以个头有点大(有空我会考虑进行改进的^_^),但是比个别程序的生成方式还是有优势的。存储和传输的时候大家可以用winrar压缩(作个免费广告,压缩率大得惊人)。
另外,重载了<<,没用上,因为笔者对标准库不熟悉,没有找到可以写入二进制的办法,所以回到了调用c函数的路上。如果你有办法可以告诉笔者吗?
不断学习中……,欢迎email交流。