这是笔者写的一个小的类,用于把一系列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交流。 [email protected] 
|