00001
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005
00006 #include "SGIMovieFile.h"
00007 #include "SGIMovieFilePrivates.h"
00008
00009 #include <vcl_fstream.h>
00010 #include <vcl_sstream.h>
00011 #include <vcl_cstdio.h>
00012 #include <vcl_cstring.h>
00013 #include <vcl_cstddef.h>
00014
00015 #include <oxp/JPEG_Decompressor.h>
00016 #include <vxl_config.h>
00017 #include <vpl/vpl_fileno.h>
00018
00019
00020 static int get_u16(vcl_istream& f);
00021 static unsigned long get_u32(vcl_istream& f);
00022
00023
00024
00025 SGIMovieFile::SGIMovieFile(char const* f)
00026 {
00027 p = new SGIMovieFilePrivates(f);
00028 }
00029
00030 SGIMovieFile::~SGIMovieFile()
00031 {
00032 delete p;
00033 }
00034
00035 int SGIMovieFile::GetLength()
00036 {
00037 return p->video_indices[0].size();
00038 }
00039
00040 vil1_image SGIMovieFile::GetImage(int)
00041 {
00042 return 0;
00043 }
00044
00045
00046 int SGIMovieFile::GetSizeX(int)
00047 {
00048 return p->width;
00049 }
00050
00051 int SGIMovieFile::GetSizeY(int)
00052 {
00053 return p->height;
00054 }
00055
00056 int SGIMovieFile::GetBitsPixel()
00057 {
00058 return 24;
00059 }
00060
00061 bool SGIMovieFile::IsInterlaced()
00062 {
00063 return p->interlaced != 0;
00064 }
00065
00066 int SGIMovieFile::GetFrameOffset(int frame_index)
00067 {
00068 return p->video_indices[0][frame_index].offset;
00069 }
00070
00071 int SGIMovieFile::GetFrameSize(int frame_index)
00072 {
00073 return p->video_indices[0][frame_index].size;
00074 }
00075
00076 bool SGIMovieFile::HasFrame(int frame_index)
00077 {
00078 return 0 <= frame_index && frame_index < GetLength();
00079 }
00080
00081 SGIMovieFilePrivates::SGIMovieFilePrivates(char const* fn):
00082 filename(fn)
00083 {
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097 vcl_ifstream f(fn);
00098 char buf[4];
00099 f.read(buf,4);
00100 if (vcl_strncmp(buf,"MOVI",4) != 0) {
00101 vcl_cerr << "SGIMovieFile: Not a movie file, magic = ["
00102 << int(buf[0]) << int(buf[1]) << int(buf[2]) << int(buf[3]) << "]\n";
00103 version = 0;
00104 return;
00105 }
00106
00107
00108 version = get_u16(f);
00109
00110 if (version == 2) {
00111
00112 if (MovieFileInterface::verbose)
00113 vcl_cerr << "SGIMovieFile: Old format, version = " << version << '\n';
00114 width = get_u16(f);
00115 height = get_u16(f);
00116 get_u16(f);
00117 } else {
00118 int version1 = get_u16(f);
00119 version = (version << 16) + version1;
00120 if (MovieFileInterface::verbose)
00121 vcl_cerr << "SGIMovieFile: New format, version = " << version << '\n';
00122
00123 get_u32(f);
00124 }
00125
00126
00127
00128
00129
00130
00131 glob = new SGIMV_Variables(f);
00132 if (MovieFileInterface::verbose) glob->print(vcl_cerr);
00133
00134 int NUM_I_TRACKS = glob->get_int("__NUM_I_TRACKS");
00135 int NUM_A_TRACKS = glob->get_int("__NUM_A_TRACKS");
00136 if (MovieFileInterface::verbose)
00137 vcl_cerr << "SGIMovieFile: Number of audio/video tracks: " << NUM_A_TRACKS << '/' << NUM_I_TRACKS << '\n';
00138
00139
00140 for (int i = 0; i < NUM_A_TRACKS; ++i)
00141 audio.push_back(SGIMV_Variables(f));
00142
00143 for (int i = 0; i < NUM_I_TRACKS; ++i)
00144 video.push_back(SGIMV_Variables(f));
00145
00146
00147 width = video[0].get_int("WIDTH");
00148 height = video[0].get_int("HEIGHT");
00149 interlaced = video[0].get_int("INTERLACING");
00150 compression = video[0].data["COMPRESSION"];
00151
00152 if (MovieFileInterface::verbose) {
00153
00154 for (int i = 0; i < NUM_A_TRACKS; ++i)
00155 audio[i].print(vcl_cerr);
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170 for (int i = 0; i < NUM_I_TRACKS; ++i)
00171 video[i].print(vcl_cerr);
00172 }
00173
00174
00175 for (int i = 0; i < NUM_A_TRACKS; ++i)
00176 audio_indices.push_back(SGIMV_FrameIndexArray(f, audio[i].get_int("__DIR_COUNT")));
00177
00178 for (int i = 0; i < NUM_I_TRACKS; ++i) {
00179 int nframes = video[i].get_int("__DIR_COUNT");
00180 video_indices.push_back(SGIMV_FrameIndexArray(f, nframes));
00181 SGIMV_FrameIndexArray& frame_indices = video_indices[i];
00182 field_indices.push_back(vcl_vector<int>(nframes * 2, 0));
00183
00184 for (int ff = 0; ff < nframes; ++ff)
00185 field_indices[i][ff*2] = frame_indices[ff].offset;
00186 }
00187
00188 SGIMV_FrameIndexArray& video_index = video_indices[0];
00189 if (MovieFileInterface::verbose) {
00190 for (unsigned i = 0; i < video_index.size(); ++i) {
00191 if (i > 10 && i < 740)
00192 continue;
00193 vcl_cerr << "SGIMovieFile: Frame " << i;
00194 if (NUM_A_TRACKS > 0)
00195 vcl_cerr << ", Audio at " << audio_indices[0][i].offset << ", size " << audio_indices[0][i].size;
00196 if (NUM_I_TRACKS > 0)
00197 vcl_cerr << ", Video at " << video_indices[0][i].offset << ", size " << video_indices[0][i].size;
00198 vcl_cerr << '\n';
00199 }
00200
00201 vcl_cerr << "SGIMovieFile: Final position, after reading header, is " << (long)f.tellg() << '\n';
00202 }
00203 }
00204
00205 bool SGIMovieFile::GetFrame(int frame_index, void* buffer)
00206 {
00207 if (p->compression != "2" && p->compression != "10") {
00208 vcl_cerr << "SGIMovieFile: Can't decompress ``" << p->compression << "'' format images\n";
00209 return false;
00210 }
00211
00212
00213 int s = p->video_indices[0][frame_index].offset;
00214
00215
00216 FILE * fp = vcl_fopen(p->filename.c_str(), "r");
00217 if (!fp) {
00218 vcl_cerr << "SGIMovieFile: File has disappeared\n";
00219 return false;
00220 }
00221
00222
00223 fseek(fp, s, SEEK_SET);
00224
00225 int in_bytes_per_pixel = 4;
00226 int bytes_per_pixel = 3;
00227 int interlace_factor = p->interlaced ? 2 : 1;
00228
00229 if (p->compression == "2")
00230 {
00231
00232 if (MovieFileInterface::verbose)
00233 vcl_cerr << "[RGB32 " << frame_index << " @ " << s << ' ';
00234
00235 int w = p->width;
00236 int h = p->height / interlace_factor;
00237 int inrowsize = w * in_bytes_per_pixel;
00238 int outrowsize = w * bytes_per_pixel;
00239 char* row_buf = new char[inrowsize];
00240
00241 char r,g,b;
00242 for (int i=0; i < interlace_factor; ++i)
00243 {
00244 if (MovieFileInterface::verbose)
00245 vcl_cerr << "fld " << i << ' ';
00246 for (int y=h-1; y >= 0; --y)
00247 {
00248 vcl_fread(row_buf, 1, inrowsize, fp);
00249 char* buf_ptr = (char*)buffer + (interlace_factor * y + i) * outrowsize;
00250 char* row_ptr = row_buf;
00251 for (int x=0; x < w; ++x) {
00252 row_ptr++;
00253 b = *(row_ptr++);
00254 g = *(row_ptr++);
00255 r = *(row_ptr++);
00256 *(buf_ptr++) = r;
00257 *(buf_ptr++) = g;
00258 *(buf_ptr++) = b;
00259 }
00260 }
00261 }
00262
00263 if (MovieFileInterface::verbose) vcl_cerr << "] ";
00264 vcl_fclose(fp);
00265 delete[] row_buf;
00266 }
00267 else if (p->compression == "10")
00268 {
00269
00270 if (MovieFileInterface::verbose)
00271 vcl_cerr << "[JPEG " << frame_index << " @ " << s << ' ';
00272
00273 JPEG_Decompressor jpeg(vpl_fileno(fp));
00274 for (int i = 0; i < interlace_factor; ++i)
00275 {
00276 if (MovieFileInterface::verbose)
00277 vcl_cerr << "fld " << i << ' ';
00278 if (i > 0) jpeg.StartNextJPEG();
00279
00280 int w = jpeg.width();
00281 int h = jpeg.height();
00282
00283 if (w != p->width) {
00284 vcl_cerr << "SGIMovieFile: Discrepancy between jpeg size and movie size. jpeg x = "
00285 << h << ", movie x = " << p->height << '\n';
00286 }
00287 if (h*interlace_factor != p->height) {
00288 vcl_cerr << "SGIMovieFile: Discrepancy between jpeg size and movie size. jpeg y = "
00289 << h << ", movie y = " << p->height << ", interlacing = " << p->interlaced << '\n';
00290 }
00291
00292 int outrowsize = w * bytes_per_pixel;
00293 for (int y = 0; y < jpeg.height(); ++y) {
00294 char *jbuf = (char*)jpeg.GetNextScanLine();
00295 if (!jbuf) {
00296 vcl_cerr << "SGIMovieFile: JPEG_Decompressor failed to load scanline " << y
00297 << ", field " << i << ", frame " << frame_index << '\n';
00298 return false;
00299 }
00300 char* bufptr = (char*)buffer + (interlace_factor * y + i) * outrowsize;
00301 vcl_memcpy(bufptr, jbuf, jpeg.width() * bytes_per_pixel);
00302 }
00303 if (MovieFileInterface::verbose)
00304 vcl_cerr << "eof " << jpeg.GetFilePosition() << ' ';
00305
00306
00307 if (p->interlaced && i == 0)
00308 p->field_indices[0][frame_index * 2 + 1] = jpeg.GetFilePosition();
00309 }
00310 if (MovieFileInterface::verbose) vcl_cerr << "] ";
00311 vcl_fclose(fp);
00312 }
00313
00314 return true;
00315 }
00316
00317 bool SGIMovieFile::GetField(int , void* )
00318 {
00319 vcl_cerr << "SGIMovieFile::GetField() not yet implemented\n";
00320 return false;
00321 #if 0 // TODO
00322 if (!p->interlaced)
00323 return GetFrame(field_index, buffer);
00324
00325
00326 if (p->compression != "10")
00327 vcl_cerr << "SGIMovieFile: Can't decompress ``" << p->compression << "'' format images\n";
00328 else
00329 {
00330 int field_start = p->field_indices[0][field_index];
00331 JPEG_Decompressor *jpeg;
00332 if (field_start == 0)
00333 {
00334 int frame_index = field_index / 2;
00335 int frame_start = p->video_indices[0][frame_index].offset;
00336
00337 p->fp->seekg(frame_start);
00338
00339 jpeg = new JPEG_Decompressor(p->fp->rdbuf()->fd());
00340 for (int y = 0; y < jpeg->height(); ++y)
00341 jpeg->GetNextScanLine();
00342
00343
00344 p->field_indices[0][frame_index * 2 + 1] = jpeg->GetFilePosition();
00345
00346 jpeg->StartNextJPEG();
00347 }
00348 else {
00349 p->fp->seekg(field_start);
00350 jpeg = new JPEG_Decompressor(p->fp->rdbuf()->fd());
00351 }
00352
00353
00354 int w = jpeg->width();
00355 int h = jpeg->height();
00356
00357 if (w != p->width) {
00358 vcl_cerr << "SGIMovieFile: Discrepancy between jpeg size and movie size. jpeg x = "
00359 << h << ", movie x = " << p->height << '\n';
00360 }
00361 if (h*2 != p->height) {
00362 vcl_cerr << "SGIMovieFile: Discrepancy between jpeg size and movie size. jpeg y = "
00363 << h << ", movie y = " << p->height << ", interlacing = " << p->interlaced << '\n';
00364 }
00365
00366 int bytes_per_pixel = GetBitsPixel() / 8;
00367 int outrowsize = w * bytes_per_pixel;
00368 for (int y = 0; y < jpeg->height(); ++y) {
00369 char *jbuf = (char*)jpeg->GetNextScanLine();
00370 if (!jbuf) {
00371 vcl_cerr << "SGIMovieFile: JPEG_Decompressor failed to load scanline " << y << ", field " << field_index << '\n';
00372 return false;
00373 }
00374 char* bufptr = (char*)buffer + y * outrowsize;
00375 vcl_memcpy(bufptr, jbuf, jpeg->width() * bytes_per_pixel);
00376 }
00377
00378 delete jpeg;
00379 }
00380 return true;
00381 #endif
00382 }
00383
00384
00385
00386 void SGIMV_Variables::read(vcl_istream& f)
00387 {
00388 #if 0
00389 struct SGIMV_Variables {
00390 word pad;
00391 word num_vars;
00392 word pad;
00393 struct VarData {
00394 byte var_buf[16];
00395 word var_size;
00396 byte var_data[var_size];
00397 };
00398 VarData data[num_vars];
00399 };
00400 #endif // 0
00401 get_u32(f);
00402 unsigned long n = get_u32(f);
00403 get_u32(f);
00404 if (n > 1000L)
00405 vcl_cerr << "SGIMovieFile: warning: A Variable list is " << n << " elements long\n";
00406 for (unsigned long i = 0; i < n; ++i)
00407 {
00408 char var_buf[17];
00409 f.read(var_buf, 16);
00410 var_buf[16] = 0;
00411
00412 vcl_size_t var_size = get_u32(f);
00413 vcl_vector<char> val_buf(var_size+1,'\0');
00414 f.read(&val_buf[0], var_size);
00415
00416 data[vcl_string(var_buf)] = vcl_string(&val_buf[0]);
00417 }
00418 }
00419
00420 int SGIMV_Variables::get_int(vcl_string const& s)
00421 {
00422 const vcl_string& v = data[s];
00423 if (v.size() == 0)
00424 return -1;
00425 vcl_stringstream vs(v);
00426 int x; vs >> x;
00427 return x;
00428 }
00429
00430 double SGIMV_Variables::get_double(vcl_string const& s)
00431 {
00432 const vcl_string& v = data[s];
00433 if (v.size() == 0)
00434 return -1.0;
00435 vcl_stringstream vs(v);
00436 double x; vs >> x;
00437 return x;
00438 }
00439
00440 vcl_ostream& SGIMV_Variables::print(vcl_ostream& s) const
00441 {
00442 s << "SGIMV_Variables: [" << data.size() << "]\n";
00443 for (VarData::const_iterator i = data.begin(); i != data.end(); ++i)
00444 s << " " << (*i).first << " = " << (*i).second << '\n';
00445 return s;
00446 }
00447
00448
00449
00450 SGIMV_FrameIndexArray::SGIMV_FrameIndexArray(vcl_istream& f, int n):
00451 vcl_vector<SGIMV_FrameIndex>(n, SGIMV_FrameIndex())
00452 {
00453 #if 0
00454 struct Index {
00455 word offset;
00456 word size;
00457 word pad;
00458 word frame;
00459 };
00460 #endif // 0
00461 for (int i = 0; i < n; ++i) {
00462 (*this)[i].offset = get_u32(f);
00463 (*this)[i].size = get_u32(f);
00464 get_u32(f);
00465 get_u32(f);
00466
00467 }
00468 }
00469
00470
00471 #if 0 // unused
00472 static void hexdump(vcl_ifstream& f, int nframes)
00473 {
00474 for (int j = 0; j < nframes; ++j) {
00475 int pos = f.tellg();
00476 vxl_uint_8 buf[16];
00477 f.read((char*)buf,16);
00478 vcl_cerr << pos << ':';
00479 for (int i = 0; i < 16; ++i) {
00480 if (i % 4 == 0) vcl_cerr << ' ';
00481 vcl_cerr << buf[i];
00482 }
00483 vcl_cerr << '\n';
00484 }
00485 }
00486 #endif
00487
00488 static int get_u16(vcl_istream& f)
00489 {
00490 vxl_uint_8 buf[2];
00491 f.read((char*)buf, 2);
00492 return (buf[0] << 8) + buf[1];
00493 }
00494
00495 static unsigned long get_u32(vcl_istream& f)
00496 {
00497 vxl_uint_8 buf[4];
00498 f.read((char*)buf, 4);
00499 return ((unsigned long)buf[0] << 24) |
00500 ((unsigned long)buf[1] << 16) |
00501 ((unsigned long)buf[2] << 8) |
00502 (unsigned long)buf[3];
00503 }