00001
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005
00006 #include "SGIMovieFileWrite.h"
00007
00008 #include <vcl_cstring.h>
00009 #include <vcl_string.h>
00010 #include <vcl_vector.h>
00011 #undef sprintf // works around a bug in libintl.h
00012 #undef fprintf
00013 #include <vcl_cstdio.h>
00014
00015 #include <vil1/vil1_jpeglib.h>
00016 #include <vil1/vil1_memory_image_of.h>
00017 #include <vil1/vil1_rgb.h>
00018
00019 const int align = 4;
00020 inline int ROUNDUP(int x)
00021 {
00022 return int((x + align - 1) / align) * align;
00023 }
00024
00025 void send4(FILE* fp, unsigned int val)
00026 {
00027 vcl_fwrite(&val, 4, 1, fp);
00028 }
00029
00030 struct Vars {
00031 vcl_vector<vcl_string> names;
00032 vcl_vector<vcl_string> values;
00033 char buf[1024];
00034
00035 void add(char const* tag, const char* value) {
00036 names.push_back(vcl_string(tag));
00037 values.push_back(vcl_string(value));
00038 }
00039
00040 void add(char const* tag, int value) {
00041 vcl_sprintf(buf, "%d", value);
00042 add(tag, buf);
00043 }
00044 void add(char const* tag, double value) {
00045 vcl_sprintf(buf, "%g", value);
00046 add(tag, buf);
00047 }
00048
00049 void send(FILE* fp) {
00050 send4(fp, 0);
00051 send4(fp, names.size());
00052 send4(fp, 0);
00053 for (unsigned i = 0; i < names.size(); ++i) {
00054
00055 int l = names[i].size();
00056 vcl_strncpy(buf, names[i].c_str(), l);
00057 while (l < 16) buf[l++] = 0;
00058 vcl_fwrite(buf, 16, 1, fp);
00059
00060 l = values[i].size();
00061 send4(fp, l + 1);
00062 vcl_fwrite(values[i].c_str(), l+1, 1, fp);
00063 }
00064 }
00065 };
00066
00067 struct SGIMovieFileWriteData {
00068 SGIMovieFileWriteData(char const* filename,
00069 int w,
00070 int h,
00071 int length);
00072
00073 void PutFrame(int i);
00074 void Finish();
00075
00076 int w,h,l;
00077 vcl_vector<unsigned> frame_ends;
00078
00079 bool interlaced;
00080
00081 vil1_memory_image_of<vil1_rgb<unsigned char> > buffer;
00082 vcl_vector<JSAMPLE*> rows;
00083 FILE *fp;
00084 int directory_pos;
00085 int first_frame;
00086
00087 jpeg_compress_struct cinfo;
00088 jpeg_error_mgr jerr;
00089
00090 void send4(unsigned int val) { vcl_fwrite(&val, 4, 1, fp); }
00091 };
00092
00093 SGIMovieFileWriteData::SGIMovieFileWriteData(char const* filename,
00094 int w_,
00095 int h_,
00096 int l_):
00097 w(w_),h(h_),l(l_),
00098 frame_ends(l, (unsigned)0),
00099 buffer(w_, h_),
00100 rows(h_)
00101 {
00102 fp = vcl_fopen(filename, "w");
00103 if (!fp) {
00104 vcl_cerr << "SGIMovieFileWriteData: Can't open " << filename << '\n';
00105 return;
00106 }
00107
00108 interlaced = true;
00109
00110
00111 vcl_fprintf(fp, "MOVI");
00112 send4(3);
00113 send4(0);
00114
00115 Vars glob;
00116 glob.add("__NUM_I_TRACKS", 1);
00117 glob.add("__NUM_A_TRACKS", 0);
00118 glob.add("LOOP_MODE", 0);
00119 glob.add("OPTIMIZED", 0);
00120 glob.add("NUM_LOOPS", 0);
00121 glob.send(fp);
00122
00123 Vars itrack;
00124 itrack.add("WIDTH", w);
00125 itrack.add("COMPRESSION", 10);
00126 itrack.add("ORIENTATION", 1100);
00127 itrack.add("Q_TEMPORAL", 0.750000);
00128 itrack.add("HEIGHT", h);
00129 itrack.add("PIXEL_ASPECT", 1.000000);
00130 itrack.add("__DIR_COUNT", l);
00131 itrack.add("INTERLACING", (int)interlaced);
00132 itrack.add("FPS", 30.000000);
00133 itrack.add("Q_SPATIAL", 0.750000);
00134 itrack.add("PACKING", 1001);
00135 itrack.send(fp);
00136
00137 directory_pos = ftell(fp);
00138 first_frame = ROUNDUP(directory_pos + 4*4*l);
00139 fseek(fp, first_frame, 0);
00140
00141
00142 cinfo.err = jpeg_std_error(&jerr);
00143
00144 jpeg_create_compress(&cinfo);
00145
00146 jpeg_stdio_dest(&cinfo, fp);
00147
00148 cinfo.image_width = w;
00149 cinfo.image_height = h / (1 + interlaced);
00150 cinfo.input_components = 3;
00151 cinfo.in_color_space = JCS_RGB;
00152
00153 jpeg_set_defaults(&cinfo);
00154
00155
00156
00157 if (interlaced)
00158 for (int y = 0; y < h / 2; ++y) {
00159 rows[y] = (JSAMPLE*)&buffer(0,2*y);
00160 rows[y + h/2] = (JSAMPLE*)&buffer(0,2*y+1);
00161 }
00162 else
00163 for (int y = 0; y < h; ++y)
00164 rows[y] = (JSAMPLE*)&buffer(0,y);
00165 }
00166
00167 struct FrameIndex {
00168 int offset;
00169 int size;
00170 int pad;
00171 int frame;
00172 };
00173
00174 void SGIMovieFileWriteData::Finish()
00175 {
00176 if (l != (int)frame_ends.size()) {
00177 vcl_cerr << "ZOKZOK: " << l << " != " << frame_ends.size() << '\n';
00178 return;
00179 }
00180 fseek(fp, directory_pos, 0);
00181 for (int i = 0; i < l; ++i) {
00182 FrameIndex f;
00183 int start = (i == 0)?first_frame : frame_ends[i-1];
00184 int end = frame_ends[i];
00185
00186 f.offset = start;
00187 f.size = end - start;
00188 f.pad = 0;
00189 f.frame = 0;
00190 vcl_fwrite(&f, 4, 4, fp);
00191 }
00192
00193 jpeg_destroy_compress(&cinfo);
00194 vcl_fclose(fp);
00195 }
00196
00197 extern unsigned long jpeg_stdio_ftell(jpeg_compress_struct*);
00198
00199 void SGIMovieFileWriteData::PutFrame(int i)
00200 {
00201 bool write_all_tables = true;
00202 if (!interlaced) {
00203 jpeg_start_compress (&cinfo, write_all_tables);
00204 jpeg_write_scanlines(&cinfo, &rows[0], h);
00205 jpeg_finish_compress(&cinfo);
00206 } else {
00207 jpeg_start_compress (&cinfo, write_all_tables);
00208 jpeg_write_scanlines(&cinfo, &rows[0], h/2);
00209 jpeg_finish_compress(&cinfo);
00210 jpeg_start_compress (&cinfo, write_all_tables);
00211 jpeg_write_scanlines(&cinfo, &rows[h/2], h/2);
00212 jpeg_finish_compress(&cinfo);
00213 }
00214
00215 int pos = ROUNDUP(ftell(fp));
00216 fseek(fp, pos, SEEK_SET);
00217 frame_ends[i] = pos;
00218 }
00219
00220
00221
00222 SGIMovieFileWrite::SGIMovieFileWrite(char const* filename,
00223 int w,
00224 int h,
00225 int length)
00226 {
00227 p = new SGIMovieFileWriteData(filename, w, h, length);
00228 }
00229
00230 SGIMovieFileWrite::~SGIMovieFileWrite()
00231 {
00232 delete p;
00233 }
00234
00235 void SGIMovieFileWrite::Finish()
00236 {
00237 p->Finish();
00238 }
00239
00240 unsigned char* SGIMovieFileWrite::GetBuffer()
00241 {
00242 return (unsigned char*)p->buffer.get_buffer();
00243 }
00244
00245 void SGIMovieFileWrite::PutBuffer(int frame_index)
00246 {
00247 p->PutFrame(frame_index);
00248 }