1 
2 
3 #if !defined(x64) && !(defined(__GNUC__) && defined(_WIN32))
4 
5 #include <cstdint>
6 
7 #include "texception.h"
8 #include "tsound.h"
9 #include "tconvert.h"
10 #include "tpropertytype.h"
11 #include "../mov/tiio_mov.h"
12 #include "movsettings.h"
13 #include "trasterimage.h"
14 #include "tsystem.h"
15 
16 #include "tiio_3gp.h"
17 
18 namespace {
19 
20 int CompressionNoneId = 0;
21 
22 class QuickTimeCleanUp;
23 class QuickTimeStuff {
24 public:
instance()25   static QuickTimeStuff *instance() {
26     if (!m_singleton) m_singleton = new QuickTimeStuff();
27     return m_singleton;
28   }
getStatus()29   OSErr getStatus() { return m_status; }
~QuickTimeStuff()30   ~QuickTimeStuff() {}
31 
32 private:
QuickTimeStuff()33   QuickTimeStuff() : m_status(noErr) {
34     m_status = InitializeQTML(0);
35     EnterMovies();
36   }
37 
38   static QuickTimeStuff *m_singleton;
39 
40   OSErr m_status;
41   friend class QuickTimeCleanUp;  // questa DEVE essere friend, cosi' posso
42                                   // controllare direttamente
43   // lo stato del singleton.
44 };
45 
46 class QuickTimeCleanUp {
47 public:
QuickTimeCleanUp()48   QuickTimeCleanUp() {}
~QuickTimeCleanUp()49   ~QuickTimeCleanUp() { /*
50                                       Nel caso si arrivasse qui senza il
51                            singleton instanziato, e si facesse direttamente
52                                       'delete QuickTimeStuff::instance();'
53                            Quicktime non farebbe in tempo a terminare le
54                                       sue routine di inizializzazione (che sono
55                            ANCHE su altri thread) e la chiamata a
56                                       TerminateQTML() causerebbe un crash
57                                       */
58 
59     if (QuickTimeStuff::m_singleton) delete QuickTimeStuff::m_singleton;
60   }
61 };
62 
63 QuickTimeCleanUp cleanUp;
64 QuickTimeStuff *QuickTimeStuff::m_singleton = 0;
65 
66 //------------------------------------------------------------------------------
67 
68 enum QTLibError {
69   QTNoError = 0x0000,
70   QTNotInstalled,
71   QTUnknownError,
72   QTUnableToOpenFile,
73   QTCantCreateFile,
74   QTUnableToGetCompressorNames,
75   QTUnableToCreateResource,
76   QTUnableToUpdateMovie,
77   QTBadTimeValue,
78   QTUnableToDoMovieTask,
79   QTUnableToSetTimeValue,
80   QTUnableToSetMovieGWorld,
81   QTUnableToSetMovieBox,
82 };
83 
buildQTErrorString(int ec)84 string buildQTErrorString(int ec) {
85   switch (ec) {
86   case QTNotInstalled:
87     return "Can't create; ensure that quicktime is correctly installed on your "
88            "machine";
89   case QTUnknownError:
90     return "Unknown error";
91   case QTUnableToOpenFile:
92     return "can't open file";
93   case QTCantCreateFile:
94     return "can't create movie";
95   case QTUnableToGetCompressorNames:
96     return "unable to get compressor name";
97   case QTUnableToCreateResource:
98     return "can't create resource";
99   case QTUnableToUpdateMovie:
100     return "unable to update movie";
101   case QTBadTimeValue:
102     return "bad frame number";
103   case QTUnableToDoMovieTask:
104     return "unable to do movie task";
105   case QTUnableToSetTimeValue:
106     return "unable to set time value";
107   case QTUnableToSetMovieGWorld:
108     return "unable to set movie graphic world";
109   case QTUnableToSetMovieBox:
110     return "unable to set movie box";
111 
112   default: { return "unknown error ('" + std::to_string(ec) + "')"; }
113   }
114 }
115 
116 //-----------------------------------------------------------
117 
AtlW2AHelper(LPSTR lpa,LPCWSTR lpw,int nChars,UINT acp)118 inline LPSTR AtlW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars, UINT acp) {
119   assert(lpw != NULL);
120   assert(lpa != NULL);
121   // verify that no illegal character present
122   // since lpa was allocated based on the size of lpw
123   // don't worry about the number of chars
124   lpa[0] = '\0';
125   WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL);
126   return lpa;
127 }
128 
129 //-----------------------------------------------------------
130 
filePath2unichar(const TFilePath & path)131 inline char *filePath2unichar(const TFilePath &path) {
132   int _convert    = 0;
133   std::wstring ws = path.getWideString();
134 
135   LPCWSTR lpw = ws.c_str();
136   char *name  = NULL;
137 
138   if (lpw) {
139     _convert   = (lstrlenW(lpw) + 1) * 2;
140     LPSTR pStr = new char[_convert];
141     name       = AtlW2AHelper(pStr, lpw, _convert, 0);
142   }
143 
144   char *outName = new char[1024];
145 
146   if (QTMLGetCanonicalPathName(name, outName, 1024) == noErr) {
147     delete[] name;
148     return outName;
149   }
150   delete[] outName;
151   return name;
152 }
153 
154 //-----------------------------------------------------------
155 
getLegalName(const TFilePath & name)156 TFilePath getLegalName(const TFilePath &name) {
157   if (QuickTimeStuff::instance()->getStatus() != noErr) return name;
158 
159   TFilePath legalName;
160   char outDir[1024];
161 
162   TFilePath dirName(name.getParentDir());
163 
164   char dirNameFp[1024] = "";
165 
166   OSErr err = QTMLGetCanonicalPathName(dirNameFp, outDir, 1024);
167 
168   if (err == noErr)
169     legalName = TFilePath(outDir) + name.withoutParentDir();
170   else
171     legalName = name;
172 
173   return legalName;
174 }
175 
176 //--------------------------`----------------------------------------------------
177 
long2fourchar(TINT32 fcc)178 string long2fourchar(TINT32 fcc) {
179   string s;
180   s += (char((fcc & 0xff000000) >> 24));
181   s += (char((fcc & 0x00ff0000) >> 16));
182   s += (char((fcc & 0x0000ff00) >> 8));
183   s += (char((fcc & 0x000000ff) >> 0));
184 
185   return s;
186 }
187 
188 const std::string CodecNamesId   = "PU_CodecName";
189 const std::string CodecQualityId = "PU_CodecQuality";
190 
191 }  // namespace
192 
193 //------------------------------------------------------------------------------
194 
195 //------------------------------------------------------------------------------
196 //  TImageWriterMov
197 //------------------------------------------------------------------------------
198 
199 class TImageWriter3gp final : public TImageWriter {
200 public:
201   TImageWriter3gp(const TFilePath &, int frameIndex, TLevelWriter3gp *);
~TImageWriter3gp()202   ~TImageWriter3gp() { m_lwm->release(); }
is64bitOutputSupported()203   bool is64bitOutputSupported() override { return false; }
204 
205 private:
206   // not implemented
207   TImageWriter3gp(const TImageWriter3gp &);
208   TImageWriter3gp &operator=(const TImageWriter3gp &src);
209 
210 public:
211   void save(const TImageP &) override;
212   int m_frameIndex;
213 
214 private:
215   TLevelWriter3gp *m_lwm;
216 };
217 
218 //-----------------------------------------------------------
219 //  TImageReaderv
220 //-----------------------------------------------------------
221 class TImageReader3gp final : public TImageReader {
222 public:
223   TImageReader3gp(const TFilePath &, int frameIndex, TLevelReader3gp *);
~TImageReader3gp()224   ~TImageReader3gp() { m_lrm->release(); }
225 
226 private:
227   // not implemented
228   TImageReader3gp(const TImageReader3gp &);
229   TImageReader3gp &operator=(const TImageReader3gp &src);
230 
231 public:
232   TImageP load() override;
233   void load(const TRasterP &rasP, const TPoint &pos = TPoint(0, 0),
234             int shrinkX = 1, int shrinkY = 1);
235   int m_frameIndex;
236 
getSize() const237   TDimension getSize() const { return m_lrm->getSize(); }
getBBox() const238   TRect getBBox() const { return m_lrm->getBBox(); }
239 
240 private:
241   TLevelReader3gp *m_lrm;
242 };
243 
244 //-----------------------------------------------------------
245 //-----------------------------------------------------------
246 //        TImageWriter3gp
247 //-----------------------------------------------------------
248 
TImageWriter3gp(const TFilePath & path,int frameIndex,TLevelWriter3gp * lwm)249 TImageWriter3gp::TImageWriter3gp(const TFilePath &path, int frameIndex,
250                                  TLevelWriter3gp *lwm)
251     : TImageWriter(path), m_lwm(lwm), m_frameIndex(frameIndex) {
252   m_lwm->addRef();
253 }
254 
255 //----------
256 namespace {
copy(TRasterP rin,PixelXRGB * bufout,int lx,int ly)257 void copy(TRasterP rin, PixelXRGB *bufout, int lx, int ly) {
258   rin->lock();
259   TRaster32P rin32 = rin;
260   assert(rin32);
261 
262   PixelXRGB *rowout = &(bufout[(ly - 1) * lx]);
263   for (int y = 0; y < rin32->getLy(); y++) {
264     PixelXRGB *pixout      = rowout;
265     TPixelRGBM32 *pixin    = rin32->pixels(y);
266     TPixelRGBM32 *pixinEnd = pixin + rin32->getLx();
267     while (pixin < pixinEnd) {
268       pixout->x = pixin->m;
269       pixout->r = pixin->r;
270       pixout->g = pixin->g;
271       pixout->b = pixin->b;
272       pixout++;
273       pixin++;
274     }
275     rowout -= lx;
276   }
277   rin->unlock();
278 }
279 };
280 
281 //-----------------------------------------------------------
save(const TImageP & img)282 void TImageWriter3gp::save(const TImageP &img) {
283   m_lwm->save(img, m_frameIndex);
284 }
285 
286 //-----------------------------------------------------------
287 
save(const TImageP & img,int frameIndex)288 void TLevelWriter3gp::save(const TImageP &img, int frameIndex) {
289   if (m_cancelled) return;
290   string msg;
291   QMutexLocker sl(&m_mutex);
292 
293   TRasterImageP image(img);
294   TRasterP ras = image->getRaster();
295   int lx       = ras->getLx();
296   int ly       = ras->getLy();
297 
298   ras->lock();
299   void *buffer = image->getRaster()->getRawData();
300   int pixSize  = image->getRaster()->getPixelSize();
301   if (pixSize != 4) {
302     msg = "Unsupported pixel type";
303     goto error;
304   }
305 
306   if (!m_properties) m_properties = new Tiio::MovWriterProperties();
307 
308   Tiio::MovWriterProperties *prop = (Tiio::MovWriterProperties *)(m_properties);
309 
310   QTAtomContainer atoms;
311   QTNewAtomContainer(&atoms);
312   fromPropertiesToAtoms(*prop, atoms);
313   ComponentInstance ci =
314       OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
315   if (SCSetSettingsFromAtomContainer(ci, atoms)) assert(false);
316 
317   /*
318 CodecType compression = prop->getCurrentCodec();
319 CodecQ quality = prop->getCurrentQuality();
320 */
321 
322   if (!m_initDone) {
323     Rect frame;
324     QDErr err;
325 
326     m_videoTrack = NewMovieTrack(m_movie, FixRatio((short)lx, 1),
327                                  FixRatio((short)ly, 1), kNoVolume);
328 
329     if ((err = GetMoviesError() != noErr)) {
330       msg = "can't create video track";
331       goto error;
332     }
333 
334     m_dataRef    = nil;
335     m_hMovieData = NewHandle(0);
336 
337     // Construct the Handle data reference
338     err = PtrToHand(&m_hMovieData, &m_dataRef, sizeof(Handle));
339 
340     if ((err = GetMoviesError() != noErr)) {
341       msg = "can't create Data Ref";
342       goto error;
343     }
344 
345     m_videoMedia =
346         NewTrackMedia(m_videoTrack, VideoMediaType, (TINT32)m_frameRate,
347                       m_dataRef, HandleDataHandlerSubType);
348 
349     OpenADefaultComponent(MovieExportType, '3gpp', &m_myExporter);
350     if (TSystem::doHaveMainLoop())
351       err = (short)MovieExportDoUserDialog(m_myExporter, m_movie, 0, 0, 0,
352                                            &m_cancelled);
353     if (m_cancelled) {
354       msg = "user abort of 3gp creation";
355       goto error;
356     }
357     if ((err = GetMoviesError() != noErr)) {
358       msg = "can't create video media";
359       goto error;
360     }
361 
362     if ((err = BeginMediaEdits(m_videoMedia)) != noErr) {
363       msg = "can't begin edit video media";
364       goto error;
365     }
366 
367     frame.left   = 0;
368     frame.top    = 0;
369     frame.right  = lx;
370     frame.bottom = ly;
371 
372     if ((err = NewGWorld(&(m_gworld), pixSize * 8, &frame, 0, 0, 0)) != noErr) {
373       msg = "can't create movie buffer";
374       goto error;
375     }
376 
377     LockPixels(m_gworld->portPixMap);
378     /*if ((err = GetMaxCompressionSize(m_gworld->portPixMap, &frame, 0,
379                     quality,  compression,anyCodec,
380                                                                                                                      &max_compressed_size))!=noErr)
381 throw TImageException(getFilePath(), "can't get max compression size");
382 */
383 
384     if ((err = MemError()) != noErr) {
385       msg = "can't allocate compressed data for movie";
386       goto error;
387     }
388 
389     if ((err = MemError()) != noErr) {
390       msg = "can't allocate img handle";
391       goto error;
392     }
393 
394     m_pixmap = GetGWorldPixMap(m_gworld);
395     if (!LockPixels(m_pixmap)) {
396       msg = "can't lock pixels";
397       goto error;
398     }
399 
400     buf    = (PixelXRGB *)(*(m_pixmap))->baseAddr;
401     buf_lx = lx;
402     buf_ly = ly;
403 
404     m_initDone = true;
405   }
406 
407   Rect frame;
408   ImageDescriptionHandle img_descr = 0;
409   Handle compressedData            = 0;
410 
411   QDErr err;
412 
413   frame.left   = 0;
414   frame.top    = 0;
415   frame.right  = lx;
416   frame.bottom = ly;
417 
418   copy(ras, buf, buf_lx, buf_ly);
419 
420   if ((err = (QDErr)SCCompressImage(ci, m_gworld->portPixMap, &frame,
421                                     &img_descr, &compressedData)) != noErr) {
422     msg = "can't compress image";
423     goto error;
424   }
425 
426   /*
427 if ((err = CompressImage(m_gworld->portPixMap,
428                          &frame,
429                                                                                            quality, compression,
430                                                                                            img_descr, compressed_data_ptr))!=noErr)
431 throw TImageException(getFilePath(), "can't compress image");
432 */
433   if ((err = AddMediaSample(
434            m_videoMedia, compressedData, 0, (*img_descr)->dataSize, 1,
435            (SampleDescriptionHandle)img_descr, 1, 0, 0)) != noErr) {
436     msg = "can't add image to movie media";
437     goto error;
438   }
439 
440   DisposeHandle((Handle)img_descr);
441   DisposeHandle(compressedData);
442   ras->unlock();
443   return;
444 
445 error:
446   ras->unlock();
447   throw TImageException(getFilePath(), msg);
448 }
449 
450 //-----------------------------------------------------------
451 //        TLevelWriterMov
452 //-----------------------------------------------------------
453 
TLevelWriter3gp(const TFilePath & path,TPropertyGroup * winfo)454 TLevelWriter3gp::TLevelWriter3gp(const TFilePath &path, TPropertyGroup *winfo)
455     : TLevelWriter(path, winfo)
456     , m_initDone(false)
457     , m_IOError(QTNoError)
458     , m_pixmap(0)
459     , m_gworld(0)
460     , m_videoMedia(0)
461     , m_videoTrack(0)
462     , m_movie(0)
463     , m_cancelled(false)
464     , m_soundDataRef(0)
465     , m_hSoundMovieData(0)
466 
467 {
468   m_frameRate = 12.;
469 
470   if (QuickTimeStuff::instance()->getStatus() != noErr) {
471     m_IOError = QTNotInstalled;
472     throw TImageException(m_path, buildQTErrorString(m_IOError));
473   }
474 
475   QDErr err;
476 
477   m_movie = NewMovie(0);
478   if ((err = GetMoviesError() != noErr))
479     throw TImageException(getFilePath(), "can't create movie");
480 }
481 
482 //-----------------------------------------------------------
483 
484 #ifdef _DEBUG
485 #define FailIf(cond, handler)                                                  \
486   if (cond) {                                                                  \
487     DebugStr((ConstStr255Param) #cond " goto " #handler);                      \
488     goto handler;                                                              \
489   } else                                                                       \
490     0
491 #else
492 #define FailIf(cond, handler)                                                  \
493   if (cond) {                                                                  \
494     goto handler;                                                              \
495   } else                                                                       \
496     0
497 #endif
498 
499 #ifdef _DEBUG
500 #define FailWithAction(cond, action, handler)                                  \
501   if (cond) {                                                                  \
502     DebugStr((ConstStr255Param) #cond " goto " #handler);                      \
503     { action; }                                                                \
504     goto handler;                                                              \
505   } else                                                                       \
506     0
507 #else
508 #define FailWithAction(cond, action, handler)                                  \
509   if (cond) {                                                                  \
510     { action; }                                                                \
511     goto handler;                                                              \
512   } else                                                                       \
513     0
514 #endif
515 
saveSoundTrack(TSoundTrack * st)516 void TLevelWriter3gp::saveSoundTrack(TSoundTrack *st) {
517   Track theTrack;
518   OSErr myErr = noErr;
519   SoundDescriptionV1Handle mySampleDesc;
520   Media myMedia;
521   Handle myDestHandle;
522   SoundComponentData sourceInfo;
523   SoundComponentData destInfo;
524   SoundConverter converter;
525   CompressionInfo compressionInfo;
526   int err;
527 
528   if (m_cancelled) return;
529 
530   if (!st) throw TException("null reference to soundtrack");
531 
532   if (st->getBitPerSample() != 16) {
533     throw TImageException(m_path, "Only 16 bits per sample is supported");
534   }
535 
536   theTrack = NewMovieTrack(m_movie, 0, 0, kFullVolume);
537   myErr    = GetMoviesError();
538 
539   FailIf(myErr != noErr, CompressErr);
540 
541   myDestHandle = NewHandle(0);
542 
543   FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest);
544 
545   *myDestHandle = (char *)st->getRawData();
546 
547   //////////
548   //
549   // create a media for the track passed in
550   //
551   //////////
552 
553   // set new track to be a sound track
554 
555   m_soundDataRef    = nil;
556   m_hSoundMovieData = NewHandle(0);
557 
558   // Construct the Handle data reference
559   err = PtrToHand(&m_hSoundMovieData, &m_soundDataRef, sizeof(Handle));
560 
561   if ((err = GetMoviesError() != noErr))
562     throw TImageException(getFilePath(), "can't create Data Ref");
563 
564   myMedia = NewTrackMedia(theTrack, SoundMediaType, st->getSampleRate(),
565                           m_soundDataRef,
566                           HandleDataHandlerSubType);  // track->rate >> 16
567   myErr = GetMoviesError();
568   FailIf(myErr != noErr, Exit);
569 
570   // start a media editing session
571   myErr = BeginMediaEdits(myMedia);
572   FailIf(myErr != noErr, Exit);
573 
574   sourceInfo.flags       = 0x0;
575   sourceInfo.format      = kSoundNotCompressed;
576   sourceInfo.numChannels = st->getChannelCount();
577   sourceInfo.sampleSize  = st->getBitPerSample();
578   sourceInfo.sampleRate  = st->getSampleRate();
579   sourceInfo.sampleCount = st->getSampleCount();
580   sourceInfo.buffer      = (unsigned char *)st->getRawData();
581   sourceInfo.reserved    = 0x0;
582 
583   destInfo.flags = kNoSampleRateConversion | kNoSampleSizeConversion |
584                    kNoSampleFormatConversion | kNoChannelConversion |
585                    kNoDecompression | kNoVolumeConversion |
586                    kNoRealtimeProcessing;
587 
588   destInfo.format = k16BitNativeEndianFormat;
589 
590   destInfo.numChannels = st->getChannelCount();
591   destInfo.sampleSize  = st->getBitPerSample();
592   destInfo.sampleRate  = st->getSampleRate();
593   destInfo.sampleCount = st->getSampleCount();
594   destInfo.buffer      = (unsigned char *)st->getRawData();
595   destInfo.reserved    = 0x0;
596 
597   SoundConverterOpen(&sourceInfo, &destInfo, &converter);
598 
599   myErr =
600       SoundConverterGetInfo(converter, siCompressionFactor, &compressionInfo);
601 
602   myErr =
603       SoundConverterGetInfo(converter, siCompressionFactor, &compressionInfo);
604   myErr = GetCompressionInfo(fixedCompression, sourceInfo.format,
605                              sourceInfo.numChannels, sourceInfo.sampleSize,
606                              &compressionInfo);
607   FailIf(myErr != noErr, ConverterErr);
608 
609   compressionInfo.bytesPerFrame =
610       compressionInfo.bytesPerPacket * destInfo.numChannels;
611 
612   //////////
613   //
614   // create a sound sample description
615   //
616   //////////
617 
618   // use the SoundDescription format 1 because it adds fields for data size
619   // information
620   // and is required by AddSoundDescriptionExtension if an extension is required
621   // for the compression format
622 
623   mySampleDesc =
624       (SoundDescriptionV1Handle)NewHandleClear(sizeof(SoundDescriptionV1));
625   FailWithAction(myErr != noErr, myErr = MemError(), Exit);
626 
627   (**mySampleDesc).desc.descSize      = sizeof(SoundDescriptionV1);
628   (**mySampleDesc).desc.dataFormat    = destInfo.format;
629   (**mySampleDesc).desc.resvd1        = 0;
630   (**mySampleDesc).desc.resvd2        = 0;
631   (**mySampleDesc).desc.dataRefIndex  = 1;
632   (**mySampleDesc).desc.version       = 1;
633   (**mySampleDesc).desc.revlevel      = 0;
634   (**mySampleDesc).desc.vendor        = 0;
635   (**mySampleDesc).desc.numChannels   = destInfo.numChannels;
636   (**mySampleDesc).desc.sampleSize    = destInfo.sampleSize;
637   (**mySampleDesc).desc.compressionID = 0;
638   (**mySampleDesc).desc.packetSize    = 0;
639   (**mySampleDesc).desc.sampleRate    = st->getSampleRate() << 16;
640   (**mySampleDesc).samplesPerPacket   = compressionInfo.samplesPerPacket;
641   (**mySampleDesc).bytesPerPacket     = compressionInfo.bytesPerPacket;
642   (**mySampleDesc).bytesPerFrame      = compressionInfo.bytesPerFrame;
643   (**mySampleDesc).bytesPerSample     = compressionInfo.bytesPerSample;
644 
645   //////////
646   //
647   // add samples to the media
648   //
649   //////////
650 
651   myErr = AddMediaSample(
652       myMedia, myDestHandle, 0,
653       destInfo.sampleCount * compressionInfo.bytesPerFrame, 1,
654       (SampleDescriptionHandle)mySampleDesc,
655       destInfo.sampleCount * compressionInfo.samplesPerPacket, 0, NULL);
656   FailIf(myErr != noErr, MediaErr);
657 
658   myErr = EndMediaEdits(myMedia);
659   FailIf(myErr != noErr, MediaErr);
660 
661   //////////
662   //
663   // insert the media into the track
664   //
665   //////////
666 
667   myErr =
668       InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
669   FailIf(myErr != noErr, MediaErr);
670 
671   goto Done;
672 
673 ConverterErr:
674 NoDest:
675 CompressErr:
676 Exit:
677 
678 Done:
679 
680 MediaErr:
681   if (mySampleDesc != NULL) DisposeHandle((Handle)mySampleDesc);
682 
683   if (converter) SoundConverterClose(converter);
684 
685   if (myErr != noErr) throw TImageException(m_path, "error saving audio track");
686 }
687 
688 //-----------------------------------------------------------
689 
~TLevelWriter3gp()690 TLevelWriter3gp::~TLevelWriter3gp() {
691   if (m_pixmap) UnlockPixels(m_pixmap);
692   if (m_gworld) DisposeGWorld(m_gworld);
693   QDErr err;
694 
695   if (m_videoMedia)
696     if ((err = EndMediaEdits(m_videoMedia)) != noErr) {
697     }  // throw TImageException(getFilePath(), "can't end edit media");
698 
699   if (m_videoTrack)
700     if ((err = InsertMediaIntoTrack(m_videoTrack, 0, 0,
701                                     GetMediaDuration(m_videoMedia), fixed1))) {
702     }  // throw TImageException(getFilePath(), "can't insert media into track");
703 
704   short resId = movieInDataForkResID;
705   if (m_movie) {
706     FSSpec fspec;
707     long myFlags      = 0L;
708     OSErr myErr       = noErr;
709     UCHAR myCancelled = FALSE;
710     char *pStr        = filePath2unichar(m_path);
711 
712     NativePathNameToFSSpec(pStr, &fspec, kFullNativePath);
713 
714     myFlags = createMovieFileDeleteCurFile;  // |
715     // movieFileSpecValid | movieToFileOnlyExport;
716 
717     myErr =
718         ConvertMovieToFile(m_movie,                 // the movie to convert
719                            NULL,                    // all tracks in the movie
720                            &fspec,                  // the output file
721                            '3gpp',                  // the output file type
722                            FOUR_CHAR_CODE('TVOD'),  // the output file creator
723                            smSystemScript,          // the script
724                            &resId,         // no resource ID to be returned
725                            myFlags,        // export flags
726                            m_myExporter);  // no specific exp
727   }
728 
729   DisposeHandle(m_hMovieData);
730   DisposeHandle(m_dataRef);
731   if (m_hSoundMovieData) DisposeHandle(m_hMovieData);
732   if (m_hSoundMovieData) DisposeHandle(m_hSoundMovieData);
733 
734   if (m_refNum) CloseMovieFile(m_refNum);
735   DisposeMovie(m_movie);
736 }
737 
738 //-----------------------------------------------------------
739 
getFrameWriter(TFrameId fid)740 TImageWriterP TLevelWriter3gp::getFrameWriter(TFrameId fid) {
741   if (m_cancelled) return 0;
742 
743   if (m_IOError) throw TImageException(m_path, buildQTErrorString(m_IOError));
744   if (fid.getLetter() != 0) return TImageWriterP(0);
745   int index = fid.getNumber() - 1;
746 
747   TImageWriter3gp *iwm = new TImageWriter3gp(m_path, index, this);
748   return TImageWriterP(iwm);
749 }
750 
751 //-----------------------------------------------------------
752 
TLevelReader3gp(const TFilePath & path)753 TLevelReader3gp::TLevelReader3gp(const TFilePath &path)
754     : TLevelReader(path)
755     , m_IOError(QTNoError)
756     , m_track(0)
757     , m_movie(0)
758     , m_depth(0)
759 //                ,m_timeScale(0)
760 {
761   FSSpec fspec;
762   QDErr err;
763   Boolean dataRefWasChanged;
764   if (QuickTimeStuff::instance()->getStatus() != noErr) {
765     m_IOError = QTNotInstalled;
766     return;
767   }
768 
769   char *pStr = filePath2unichar(m_path);
770 
771   if ((err = NativePathNameToFSSpec(pStr, &fspec, kFullNativePath)) != noErr) {
772     delete[] pStr;
773     pStr = 0;
774     throw TImageException(path, "can't open file");
775   }
776   delete[] pStr;
777   pStr = 0;
778   if ((err = OpenMovieFile(&fspec, &m_refNum, fsRdPerm))) {
779     m_IOError = QTUnableToOpenFile;
780     return;
781   }
782 
783   m_resId = 0;
784   Str255 name;
785   err = NewMovieFromFile(&m_movie, m_refNum, &m_resId, name, fsRdPerm,
786                          &dataRefWasChanged);
787 
788   int numTracks = GetMovieTrackCount(m_movie);
789   assert(numTracks == 1 || numTracks == 2);
790 
791   m_track =
792       GetMovieIndTrackType(m_movie, 1, VideoMediaType, movieTrackMediaType);
793 
794   // m_track=GetMovieTrack(m_movie,numTracks);
795 
796   ImageDescriptionHandle imageH;
797   imageH = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
798   TINT32 index   = 1;
799   Media theMedia = GetTrackMedia(m_track);
800 
801   GetMediaSampleDescription(theMedia, index, (SampleDescriptionHandle)imageH);
802   ImageDescriptionPtr imagePtr = *imageH;
803   m_lx                         = imagePtr->width;
804   m_ly                         = imagePtr->height;
805   m_depth                      = imagePtr->depth;
806 
807   DisposeHandle((Handle)imageH);
808 
809   // m_info->m_frameRate = GetMediaTimeScale(theMedia);
810 }
811 
812 //------------------------------------------------
813 
814 //------------------------------------------------
815 
816 //------------------------------------------------
817 // TImageReaderMov
818 //------------------------------------------------
819 
TImageReader3gp(const TFilePath & path,int frameIndex,TLevelReader3gp * lrm)820 TImageReader3gp::TImageReader3gp(const TFilePath &path, int frameIndex,
821                                  TLevelReader3gp *lrm)
822     : TImageReader(path), m_lrm(lrm), m_frameIndex(frameIndex) {
823   m_lrm->addRef();
824 }
825 
826 //------------------------------------------------
827 
~TLevelReader3gp()828 TLevelReader3gp::~TLevelReader3gp() {
829   StopMovie(m_movie);
830 
831   if (m_refNum) CloseMovieFile(m_refNum);
832   if (m_movie) DisposeMovie(m_movie);
833 }
834 
835 //------------------------------------------------
836 
loadInfo()837 TLevelP TLevelReader3gp::loadInfo() {
838   TLevelP level;
839   if (m_IOError != QTNoError)
840     throw TImageException(m_path, buildQTErrorString(m_IOError));
841 
842   OSType mediaType = VisualMediaCharacteristic;
843   TimeValue nextTime, currentTime;
844   currentTime = 0;
845   nextTime    = 0;
846   // per il primo
847   int f = 1;
848   // io vorrei fare '|', ma sul manuale c'e' scritto '+'
849   GetMovieNextInterestingTime(m_movie, nextTimeMediaSample + nextTimeEdgeOK, 1,
850                               &mediaType, currentTime, 0, &nextTime, 0);
851   if (nextTime != -1) {
852     TFrameId frame(f);
853     level->setFrame(frame, TImageP());
854     currentTimes.push_back(nextTime);
855     f++;
856   }
857   currentTime = nextTime;
858   while (nextTime != -1) {
859     GetMovieNextInterestingTime(m_movie, nextTimeMediaSample, 1, &mediaType,
860                                 currentTime, 0, &nextTime, 0);
861     if (nextTime != -1) {
862       TFrameId frame(f);
863       level->setFrame(frame, TImageP());
864       currentTimes.push_back(nextTime);
865       f++;
866     }
867     currentTime = nextTime;
868   }
869   return level;
870 }
871 
872 //------------------------------------------------
873 
load()874 TImageP TImageReader3gp::load() {
875   TRasterPT<TPixelRGBM32> ret(m_lrm->getSize());
876 
877   m_lrm->load(ret, m_frameIndex, TPointI(), 1, 1);
878 
879   return TRasterImageP(ret);
880 }
881 
882 //------------------------------------------------
883 
load(const TRasterP & rasP,const TPoint & pos,int shrinkX,int shrinkY)884 void TImageReader3gp::load(const TRasterP &rasP, const TPoint &pos, int shrinkX,
885                            int shrinkY) {
886   if ((shrinkX != 1) || (shrinkY != 1) || (pos != TPoint(0, 0))) {
887     TImageReader::load(rasP, pos, shrinkX, shrinkY);
888   } else
889     m_lrm->load(rasP, m_frameIndex, pos, shrinkX, shrinkY);
890 }
891 //------------------------------------------------
setMatteAndYMirror(const TRaster32P & ras)892 inline void setMatteAndYMirror(const TRaster32P &ras) {
893   ras->lock();
894   TPixel32 *upRow = ras->pixels();
895   TPixel32 *dwRow = ras->pixels(ras->getLy() - 1);
896   int hLy  = (int)(ras->getLy() / 2. + 0.5);  // piccola pessimizzazione...
897   int wrap = ras->getWrap();
898   int lx   = ras->getLx();
899   TPixel32 *upPix   = 0;
900   TPixel32 *lastPix = ras->pixels(hLy);
901   while (upPix < lastPix) {
902     upPix            = upRow;
903     TPixel32 *dwPix  = dwRow;
904     TPixel32 *endPix = upPix + lx;
905     while (upPix < endPix) {
906       TPixel32 tmpPix(upPix->r, upPix->g, upPix->b, 0xff);
907       *upPix   = *dwPix;
908       upPix->m = 0xff;
909       *dwPix   = tmpPix;
910       ++upPix;
911       ++dwPix;
912     }
913     upRow += wrap;
914     dwRow -= wrap;
915   }
916   ras->unlock();
917 }
918 
919 //------------------------------------------------
920 
load(const TRasterP & rasP,int frameIndex,const TPoint & pos,int shrinkX,int shrinkY)921 void TLevelReader3gp::load(const TRasterP &rasP, int frameIndex,
922                            const TPoint &pos, int shrinkX, int shrinkY) {
923   {
924     QMutexLocker sl(&m_mutex);
925     if (m_IOError != QTNoError)
926       throw TImageException(m_path, buildQTErrorString(m_IOError));
927 
928     TRaster32P ras = rasP;
929 
930     Rect rect;
931     rect.right  = pos.x + ras->getLx();
932     rect.left   = pos.x;
933     rect.bottom = pos.y + ras->getLy();
934     rect.top    = pos.y;
935 
936     GWorldPtr offscreenGWorld;
937     OSErr err;
938     ras->lock();
939     err = QTNewGWorldFromPtr(&offscreenGWorld, k32BGRAPixelFormat, &rect, 0, 0,
940                              0, ras->getRawData(), ras->getWrap() * 4);
941 
942     if (err != noErr) {
943       m_IOError = QTUnableToCreateResource;
944       DisposeGWorld(offscreenGWorld);
945       goto error;
946     }
947 
948     SetMovieBox(m_movie, &rect);
949     err = GetMoviesError();
950     if (err != noErr) {
951       m_IOError = QTUnableToSetMovieBox;
952       DisposeGWorld(offscreenGWorld);
953       goto error;
954     }
955 
956     SetMovieGWorld(m_movie, offscreenGWorld, GetGWorldDevice(offscreenGWorld));
957     err = GetMoviesError();
958     if (err != noErr) {
959       m_IOError = QTUnableToSetMovieGWorld;
960       DisposeGWorld(offscreenGWorld);
961       goto error;
962     }
963 
964     TimeValue currentTime = currentTimes[frameIndex];
965 
966     SetMovieTimeValue(m_movie, currentTime);
967 
968     err = GetMoviesError();
969     if (err != noErr) {
970       m_IOError = QTUnableToSetTimeValue;
971       DisposeGWorld(offscreenGWorld);
972       goto error;
973     }
974 
975     err = UpdateMovie(m_movie);
976     if (err != noErr) {
977       m_IOError = QTUnableToUpdateMovie;
978       DisposeGWorld(offscreenGWorld);
979       goto error;
980     }
981 
982     MoviesTask(m_movie, 0);
983     err = GetMoviesError();
984     if (err != noErr) {
985       m_IOError = QTUnableToDoMovieTask;
986       DisposeGWorld(offscreenGWorld);
987       goto error;
988     }
989 
990     DisposeGWorld(offscreenGWorld);
991     ras->unlock();
992   }
993 
994   if (m_depth != 32) {
995     setMatteAndYMirror(rasP);
996   } else {
997     rasP->yMirror();
998   }
999 
1000   return;
1001 
1002 error:
1003   rasP->unlock();
1004   throw TImageException(m_path, buildQTErrorString(m_IOError));
1005 }
1006 
1007 //------------------------------------------------
1008 
getFrameReader(TFrameId fid)1009 TImageReaderP TLevelReader3gp::getFrameReader(TFrameId fid) {
1010   if (m_IOError != QTNoError)
1011     throw TImageException(m_path, buildQTErrorString(m_IOError));
1012 
1013   if (fid.getLetter() != 0) return TImageReaderP(0);
1014   int index = fid.getNumber() - 1;
1015 
1016   TImageReader3gp *irm = new TImageReader3gp(m_path, index, this);
1017   return TImageReaderP(irm);
1018 }
1019 
1020 #endif
1021