00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #ifndef MEDIA_QUARTZQUICKTIMEDECODER_C_DEFINED
00045 #define MEDIA_QUARTZQUICKTIMEDECODER_C_DEFINED
00046
00047 #include "Media/QuartzQuickTimeDecoder.H"
00048
00049 #ifdef HAVE_QUICKTIME_QUICKTIME_H
00050
00051 #include "Image/Image.H"
00052 #include "Image/Pixels.H"
00053 #include "Raster/GenericFrame.H"
00054 #include "Util/log.H"
00055
00056
00057 static GenericFrame PixelBuffer2GenericFrame(CVImageBufferRef inImage)
00058 {
00059 if (NULL == inImage) return GenericFrame();
00060
00061 const byte* const baseAddress =
00062 static_cast<const byte*>(CVPixelBufferGetBaseAddress(inImage));
00063 const size_t bytesPerRow = CVPixelBufferGetBytesPerRow(inImage);
00064 const size_t width = CVPixelBufferGetWidth(inImage);
00065 const size_t height = CVPixelBufferGetHeight(inImage);
00066
00067 const OSType inPixelFormat =
00068 CVPixelBufferGetPixelFormatType((CVPixelBufferRef)inImage);
00069
00070 LDEBUG("PixelBuffer FormatType: %lx", inPixelFormat);
00071
00072 switch(inPixelFormat)
00073 {
00074 case k32ARGBPixelFormat:
00075
00076 LDEBUG("baseAddress=%p", baseAddress);
00077 LDEBUG("bytesPerRow=%u", (unsigned int) bytesPerRow);
00078
00079 {
00080 Image<PixRGB<byte> > rgb(width, height, NO_INIT);
00081 byte* data = reinterpret_cast<byte*>(rgb.getArrayPtr());
00082
00083 for (size_t y = 0; y < height; ++y)
00084 {
00085 const byte* row = baseAddress + y*bytesPerRow;
00086 for (size_t x = 0; x < width; ++x)
00087 {
00088
00089 data[0] = row[1];
00090 data[1] = row[2];
00091 data[2] = row[3];
00092 data += 3;
00093 row += 4;
00094 }
00095 }
00096
00097 return GenericFrame(rgb);
00098 }
00099
00100 break;
00101 default:
00102 LFATAL("I don't know what to do with this format!");
00103 break;
00104 }
00105
00106 return GenericFrame();
00107 }
00108
00109
00110 static void SetNumberValue(CFMutableDictionaryRef inDict,
00111 CFStringRef inKey, SInt32 inValue)
00112 {
00113 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue);
00114 if (NULL == number)
00115 LFATAL("CFNumberCreate() failed");
00116
00117 CFDictionarySetValue(inDict, inKey, number);
00118
00119 CFRelease(number);
00120 }
00121
00122
00123
00124
00125
00126 static QTVisualContextRef CreatePixelBufferContext(SInt32 inPixelFormat,
00127 const CGRect* inBounds)
00128 {
00129 if (0 == inPixelFormat)
00130 LFATAL("pixel format must be non-zero");
00131
00132 if (CGRectIsNull(*inBounds))
00133 LFATAL("bounds rect must be non-empty");
00134
00135
00136 Janitor<CFMutableDictionaryRef> pixelBufferOptions
00137 (CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
00138 &kCFTypeDictionaryKeyCallBacks,
00139 &kCFTypeDictionaryValueCallBacks),
00140 &CFRelease);
00141
00142 if (NULL == pixelBufferOptions.it)
00143 LFATAL("couldn't create pixelBufferOptions");
00144
00145
00146 SetNumberValue(pixelBufferOptions.it, kCVPixelBufferPixelFormatTypeKey, inPixelFormat);
00147
00148
00149 SetNumberValue(pixelBufferOptions.it, kCVPixelBufferWidthKey, int(inBounds->size.width));
00150 SetNumberValue(pixelBufferOptions.it, kCVPixelBufferHeightKey, int(inBounds->size.height));
00151
00152
00153 SetNumberValue(pixelBufferOptions.it, kCVPixelBufferBytesPerRowAlignmentKey, 16);
00154
00155
00156 Janitor<CFMutableDictionaryRef> visualContextOptions
00157 (CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
00158 &kCFTypeDictionaryKeyCallBacks,
00159 &kCFTypeDictionaryValueCallBacks),
00160 &CFRelease);
00161
00162 if (NULL == visualContextOptions.it)
00163 LFATAL("couldn't create visualContextOptions");
00164
00165
00166 CFDictionarySetValue(visualContextOptions.it,
00167 kQTVisualContextPixelBufferAttributesKey,
00168 pixelBufferOptions.it);
00169
00170
00171 QTVisualContextRef outVisualContext;
00172 if (noErr != QTPixelBufferContextCreate(kCFAllocatorDefault,
00173 visualContextOptions.it,
00174 &outVisualContext))
00175 LFATAL("couldn't create visualContext");
00176
00177 if (NULL == outVisualContext)
00178 LFATAL("newly created visualContext was null");
00179
00180 return outVisualContext;
00181 }
00182
00183
00184
00185
00186
00187
00188
00189 void QuartzQuickTimeDecoder::
00190 imageAvailableCallback(QTVisualContextRef visualContext,
00191 const CVTimeStamp* timeStamp, void* refCon)
00192 {
00193
00194 LDEBUG("CVTimeStamp Flags: %llu", timeStamp->flags);
00195
00196 if (timeStamp->flags & kCVTimeStampVideoTimeValid)
00197 LDEBUG("CVTimeStamp VideoTime: %lld", timeStamp->videoTime);
00198
00199 if (timeStamp->flags & kCVTimeStampHostTimeValid)
00200 LDEBUG("CVTimeStamp HostTime: %llu", timeStamp->hostTime);
00201
00202 if (timeStamp->flags & kCVTimeStampRateScalarValid)
00203 LDEBUG("CVTimeStamp Rate: %1.0g", timeStamp->rateScalar);
00204
00205 QuartzQuickTimeDecoder* qqd = (QuartzQuickTimeDecoder*) refCon;
00206
00207
00208 if (!QTVisualContextIsNewImageAvailable(qqd->itsVisualContext.it, timeStamp))
00209 {
00210 qqd->itsCallbackError = "no new image was available";
00211 }
00212 else
00213 {
00214 CVImageBufferRef newImage = NULL;
00215 if (noErr != QTVisualContextCopyImageForTime(qqd->itsVisualContext.it,
00216 kCFAllocatorDefault, timeStamp,
00217 &newImage))
00218 {
00219 qqd->itsCallbackError = "QTVisualContextCopyImageForTime() failed";
00220 }
00221 else if (NULL == newImage)
00222 {
00223 qqd->itsCallbackError =
00224 "QTVisualContextCopyImageForTime() gave a null image";
00225 }
00226 else if (noErr != (CVPixelBufferLockBaseAddress
00227 ((CVPixelBufferRef)newImage, 0)))
00228 {
00229 qqd->itsCallbackError = "CVPixelBufferLockBaseAddress failed";
00230 }
00231 else
00232 {
00233
00234 try
00235 {
00236 qqd->itsFrame = PixelBuffer2GenericFrame(newImage);
00237 ++qqd->itsFrameNumber;
00238 }
00239 catch (std::exception& e)
00240 {
00241 qqd->itsCallbackError = e.what();
00242 }
00243 catch (...)
00244 {
00245 qqd->itsCallbackError = "unknown error";
00246 }
00247
00248 CVPixelBufferUnlockBaseAddress((CVPixelBufferRef)newImage, 0);
00249 }
00250
00251 if (newImage)
00252 CVPixelBufferRelease(newImage);
00253 }
00254
00255 QTVisualContextTask(qqd->itsVisualContext.it);
00256 }
00257
00258
00259 QuartzQuickTimeDecoder::QuartzQuickTimeDecoder(const char* fname)
00260 :
00261 itsMovie(NULL, &DisposeMovie),
00262 itsVisualContext(NULL, &QTVisualContextRelease),
00263 itsFrame(),
00264 itsFrameNumber(0),
00265 itsNextTime(0),
00266 itsNextFramePushback(false),
00267 itsFirstFrame(true)
00268 {
00269
00270 EnterMovies();
00271
00272
00273 CFStringRef inPath = CFStringCreateWithCString(NULL, fname,
00274 CFStringGetSystemEncoding());
00275 if (!inPath)
00276 LFATAL("Could not get CFString from %s", fname);
00277
00278
00279 Janitor<Handle> myDataRef(NULL, &DisposeHandle);
00280 OSType myDataRefType;
00281 if (noErr != QTNewDataReferenceFromFullPathCFString
00282 (inPath, (unsigned long) kQTNativeDefaultPathStyle,
00283 0, &myDataRef.it, &myDataRefType))
00284 LFATAL("Could not get DataRef for %s", fname);
00285
00286
00287 short actualResId = DoTheRightThing;
00288 if (noErr != NewMovieFromDataRef(&itsMovie.it, newMovieActive,
00289 &actualResId, myDataRef.it, myDataRefType))
00290 LFATAL("Could not get Movie from DataRef for %s", fname);
00291
00292
00293 Rect bounds;
00294 GetMovieBox(itsMovie.it, &bounds);
00295
00296 HIRect theBounds;
00297 theBounds.origin.x = bounds.left;
00298 theBounds.origin.y = bounds.top;
00299 theBounds.size.width = bounds.right - bounds.left;
00300 theBounds.size.height = bounds.bottom - bounds.top;
00301
00302 itsDims = Dims(int(theBounds.size.width), int(theBounds.size.height));
00303
00304 LDEBUG("theBounds2 = {%f, %f, %f, %f}\n",
00305 theBounds.origin.x, theBounds.origin.y,
00306 theBounds.size.width, theBounds.size.height);
00307
00308 itsVisualContext.it =
00309 CreatePixelBufferContext(k32ARGBPixelFormat, &theBounds);
00310
00311 if (noErr != SetMovieVisualContext(itsMovie.it, itsVisualContext.it))
00312 LFATAL("SetMovieVisualContext failed");
00313
00314
00315 if (noErr !=
00316 QTVisualContextSetImageAvailableCallback
00317 (itsVisualContext.it, &QuartzQuickTimeDecoder::imageAvailableCallback,
00318 this))
00319 LFATAL("QTVisualContextSetImageAvailableCallback failed");
00320 }
00321
00322
00323 QuartzQuickTimeDecoder::~QuartzQuickTimeDecoder()
00324 {}
00325
00326
00327 int QuartzQuickTimeDecoder::apparentFrameNumber() const
00328 {
00329 return
00330 itsNextFramePushback
00331 ? itsFrameNumber - 1
00332 : itsFrameNumber;
00333 }
00334
00335
00336 GenericFrameSpec QuartzQuickTimeDecoder::peekFrameSpec()
00337 {
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350 GenericFrameSpec result;
00351
00352 result.nativeType = GenericFrame::RGB_U8;
00353 result.videoFormat = VIDFMT_AUTO;
00354 result.videoByteSwap = false;
00355 result.dims = itsDims;
00356 result.floatFlags = 0;
00357
00358 return result;
00359 }
00360
00361
00362 VideoFrame QuartzQuickTimeDecoder::readVideoFrame()
00363 {
00364 return this->readFrame().asVideo();
00365 }
00366
00367
00368 Image<PixRGB<byte> > QuartzQuickTimeDecoder::readRGB()
00369 {
00370 return this->readFrame().asRgb();
00371 }
00372
00373
00374 bool QuartzQuickTimeDecoder::readAndDiscardFrame()
00375 {
00376 return (this->readFrame().initialized());
00377 }
00378
00379
00380 GenericFrame QuartzQuickTimeDecoder::readFrame()
00381 {
00382 if (itsNextTime < 0)
00383 return GenericFrame();
00384
00385
00386
00387 if (itsNextFramePushback)
00388 {
00389 itsNextFramePushback = false;
00390 ASSERT(itsFrame.initialized());
00391 const GenericFrame f = itsFrame;
00392 itsFrame = GenericFrame();
00393 return f;
00394 }
00395
00396 const int oldFrameNumber = itsFrameNumber;
00397
00398 if (itsFirstFrame)
00399 {
00400 PrerollMovie(itsMovie.it, 0, fixed1);
00401
00402 MoviesTask(itsMovie.it, 0);
00403 }
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415 const bool gotframe = (itsFrameNumber != oldFrameNumber);
00416
00417 TimeValue current = GetMovieTime(itsMovie.it, NULL);
00418 GetMovieNextInterestingTime(itsMovie.it, nextTimeStep, 0, NULL,
00419 current, fixed1, &itsNextTime, NULL);
00420
00421 LDEBUG("current=%ld next=%ld", current, itsNextTime);
00422
00423
00424
00425 if (!gotframe)
00426 MoviesTask(itsMovie.it, 0);
00427
00428
00429
00430
00431
00432
00433 while (itsFrameNumber == oldFrameNumber && itsCallbackError.length() == 0)
00434 {
00435 LDEBUG("waiting for drawCompleteCallback");
00436 usleep(10000);
00437 }
00438
00439 if (itsCallbackError.length() > 0)
00440 {
00441 const std::string s = itsCallbackError;
00442 itsCallbackError = std::string();
00443 LFATAL("error during callback: %s", s.c_str());
00444 }
00445
00446 LDEBUG("frame #%d @ %ld", itsFrameNumber, current);
00447
00448 if (itsNextTime >= 0)
00449 SetMovieTimeValue(itsMovie.it, itsNextTime);
00450
00451 itsFirstFrame = false;
00452
00453 ASSERT(itsFrame.initialized());
00454 const GenericFrame f = itsFrame;
00455 itsFrame = GenericFrame();
00456 return f;
00457 }
00458
00459 #endif // HAVE_QUICKTIME_QUICKTIME_H
00460
00461
00462
00463
00464
00465
00466
00467
00468 #endif // MEDIA_QUARTZQUICKTIMEDECODER_C_DEFINED