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