// CompressRLE
//		Main compress routine, this function will call the appropriate RLE compression
// method depending on the pixel depth of the source image.
OSErr CompressPixMapRLE(PixMapHandle pixMapHdl, Ptr compressBuffer, Size *compressBufferSizePtr)
{
	Handle hdl = NULL;
	Ptr	   tempPtr = NULL,srcData;
	Ptr	   pixBaseAddr = GetPixBaseAddr(pixMapHdl);
	OSType pixelFormat = GETPIXMAPPIXELFORMAT(*pixMapHdl);
	int		depth = QTGetPixelSize(pixelFormat);
	long   rowBytes = QTGetPixMapHandleRowBytes(pixMapHdl);
	int		width = (**pixMapHdl).bounds.right - (**pixMapHdl).bounds.left;
	int		i, height = (**pixMapHdl).bounds.bottom - (**pixMapHdl).bounds.top;
	Size   widthByteSize = (depth * (long)width + 7) >> 3;
	OSErr  err = noErr;

	// need to remove padding between rows?
	if(widthByteSize != rowBytes){
		// Make a temp buffer for the source
		hdl = NewHandle(height * widthByteSize);
		err = MemError();
		if (err) goto bail;
		
		HLock(hdl);
		
		srcData = tempPtr = *hdl;
		
		// Get rid of row bytes padding
		for (i = 0; i < height; i++) {
			BlockMoveData(pixBaseAddr, tempPtr, widthByteSize);
			tempPtr += widthByteSize;
			pixBaseAddr += rowBytes;
		}
	}else
		srcData = pixBaseAddr;

	// Compress
	switch (depth) {
	case 1:
		CompressRLE8((UInt8*)srcData, height * widthByteSize, compressBuffer, compressBufferSizePtr);
		break;
	case 8:
		CompressRLE8((UInt8*)srcData, height * widthByteSize, compressBuffer, compressBufferSizePtr);
		break;
	case 16:
		CompressRLE16((UInt16*)srcData, height * (widthByteSize >> 1), compressBuffer, compressBufferSizePtr);
		break;
	case 32:
		CompressRLE32((UInt32*)srcData, height * (widthByteSize >> 2), compressBuffer, compressBufferSizePtr);
		break;
	}
	
bail:
	if (hdl)
		DisposeHandle(hdl);

	return err;
}
////////////////////////////////////////////////////////////////////////////////
// virtual
int LLMediaImplQuickTime::getMediaDataWidth() const
{
	if ( mGWorldHandle )
	{
		int depth = getMediaDepth();

		if (depth < 1)
			depth = 1;

		// ALWAYS use the row bytes from the PixMap if we have a GWorld because
		// sometimes it's not the same as mMediaDepth * mMediaWidth !
		PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
		return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth;
	}
	else
	{
		return LLMediaImplCommon::getMediaDataWidth();
	}
}
////////////////////////////////////////////////////////////////////////////////
// virtual
bool LLMediaImplQuickTime::sizeChanged()
{
	if ( ! mMovieHandle )
		return false;

	// sanitize size of movie
	Rect movie_rect;
	setMovieBoxEnhanced( &movie_rect );

	// we need this later
	int width = ( movie_rect.right - movie_rect.left );
	int height = ( movie_rect.bottom - movie_rect.top );

	std::cout << "LLMEDIA> size changed to " << width << " x " << height << std::endl;

	setMediaSize( width, height );

	// media depth won't change
	int depth_bits = getMediaDepth() * 8;

	GWorldPtr old_gworld_handle = mGWorldHandle;

	if (old_gworld_handle)
	{
		GWorldFlags result = UpdateGWorld( &mGWorldHandle, depth_bits, &movie_rect, NULL, NULL, 0 );
		if ( gwFlagErr == result )
		{
			// TODO: unrecoverable?? throw exception?  return something?
			return false;
		}
	}
	else
	{
		OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &movie_rect, NULL, NULL, keepLocal | pixelsLocked );
		if ( noErr != result )
		{
			// ATODO: unrecoverable??  throw exception?  return something?
			return false;
		}

		// clear memory in GWorld to avoid random screen visual fuzz from uninitialized texture data
		if ( mGWorldHandle )
		{
			PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
			unsigned char* ptr = ( unsigned char* )GetPixBaseAddr( pix_map_handle );
			memset( ptr, 0x00, height * QTGetPixMapHandleRowBytes( pix_map_handle ) );
		}
	}

	// point movie at GWorld if it's new
	if ( mMovieHandle && ! old_gworld_handle )
	{
		SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice ( mGWorldHandle ) );
	}

	// update movie controller
	if ( mMovieController )
	{
		MCSetControllerPort( mMovieController, mGWorldHandle );
		MCPositionController( mMovieController, &movie_rect, &movie_rect,
							  mcTopLeftMovie | mcPositionDontInvalidate );
		MCMovieChanged( mMovieController, mMovieHandle );
	}

	// Emit event with size change so the calling app knows about it too
	LLMediaEvent event( this );
	mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event );

	return true;
}
Example #4
0
// ######################################################################
OSErr QuickTimeGrabber::Impl::grabData(SGChannel c, Ptr p,
                                       long len, TimeValue time)
{
  if (itsGotFrame)
    {
      LDEBUG("already got a frame on this iteration");
      return noErr;
    }

  // we only care about the video
  if (c != itsSGChanVideo.it)
    {
      return noErr;
    }

  // reset frame and time counters after a stop/start
  if (time < itsPrevTime)
    {
      LDEBUG("resetting frame/time counters (current=%ld, last=%ld)",
             (long) time, (long) itsPrevTime);
      itsPrevTime = 0;
      itsFrameCount = 0;
    }

  if (itsTimeScale == 0)
    {
      LDEBUG("setting up time scale & timebase");

      Fixed framesPerSecond;
      long  milliSecPerFrameIgnore, bytesPerSecondIgnore;

      // first time here so get the time scale & timebase
      if (noErr != SGGetChannelTimeScale(c, &itsTimeScale))
        {
          itsErrorMsg = "SGGetChannelTimeScale() failed";
          return OSErr(-1);
        }

      if (noErr != SGGetTimeBase(itsSeqGrab.it, &itsTimeBase))
        {
          itsErrorMsg = "SGGetTimeBase() failed";
          return OSErr(-1);
        }

      if (noErr != VDGetDataRate(SGGetVideoDigitizerComponent(c),
                                 &milliSecPerFrameIgnore,
                                 &framesPerSecond,
                                 &bytesPerSecondIgnore))
        {
          itsErrorMsg = "VDGetDataRate() failed";
          return OSErr(-1);
        }
    }

  if (itsDrawSeq == 0)
    {
      LDEBUG("setting up decompression sequence");

      // set up decompression sequence
      ImageDescriptionHandle imageDesc =
        (ImageDescriptionHandle)NewHandle(0);

      // retrieve a channel's current sample description, the channel
      // returns a sample description that is appropriate to the type
      // of data being captured
      if (noErr != SGGetChannelSampleDescription(c, (Handle)imageDesc))
        {
          itsErrorMsg = "SGGetChannelSampleDescription() failed";
          return OSErr(-1);
        }

      // make a scaling matrix for the sequence
      Rect sourceRect = { 0, 0 };
      sourceRect.right = (**imageDesc).width;
      sourceRect.bottom = (**imageDesc).height;

      Rect scaleRect;
      GetPixBounds(GetGWorldPixMap(itsGWorld), &scaleRect);

      // if DV do high quality 720x480 both fields
      CodecFlags cFlags =
        (kDVCNTSCCodecType == (**imageDesc).cType)
        ? codecHighQuality
        : codecNormalQuality;

      MatrixRecord scaleMatrix;
      RectMatrix(&scaleMatrix, &sourceRect, &scaleRect);

      LINFO("sourceRect = %dx%d, scaleRect = %dx%d",
            sourceRect.right - sourceRect.left,
            sourceRect.bottom - sourceRect.top,
            scaleRect.right - scaleRect.left,
            scaleRect.bottom - scaleRect.top);

      // begin the process of decompressing a sequence of frames this
      // is a set-up call and is only called once for the sequence -
      // the ICM will interrogate different codecs and construct a
      // suitable decompression chain, as this is a time consuming
      // process we don't want to do this once per frame (eg. by using
      // DecompressImage) for more information see Ice Floe #8
      // http://developer.apple.com/quicktime/icefloe/dispatch008.html
      // the destination is specified as the GWorld
      CGrafPtr dest = itsGWorld;
      if (noErr != DecompressSequenceBeginS
          (&itsDrawSeq,     // pointer to field to receive unique ID for sequence
           imageDesc,       // handle to image description structure
           p,               // points to the compressed image data
           len,             // size of the data buffer
           dest,            // port for the DESTINATION image
           NULL,            // graphics device handle, if port is set, set to NULL
           NULL,            // decompress the entire source image - no source extraction
           &scaleMatrix,    // transformation matrix
           srcCopy,         // transfer mode specifier
           (RgnHandle)NULL, // clipping region in dest. coordinate system to use as a mask
           0,               // flags
           cFlags,          // accuracy in decompression
           bestSpeedCodec)) // compressor identifier or special identifiers ie. bestSpeedCodec
        {
          itsErrorMsg = "DSeqBegin failed";
          return OSErr(-1);
        }

      DisposeHandle((Handle)imageDesc);

    } // itsDrawSeq == 0

  // get the TimeBase time and figure out the delta between that time
  // and this frame time
  const TimeValue timeBaseTime = GetTimeBaseTime(itsTimeBase,
                                                 itsTimeScale, NULL);
  const TimeValue timeBaseDelta = timeBaseTime - time;
  const TimeValue frameTimeDelta = time - itsPrevTime;

  if (timeBaseDelta < 0)
    {
      itsErrorMsg = "bogus timeBaseDelta";
      return OSErr(-1);
    }

  // if we have more than one queued frame and our capture rate drops
  // below 10 frames, skip the frame to try and catch up
  if ((itsQueuedFrameCount > 1)
      &&  ((itsTimeScale / frameTimeDelta) < 10)
      && (itsSkipFrameCount < 15))
    {
      LDEBUG("dropping frame");
      ++itsSkipFrameCount;
      ++itsSkipFrameCountTotal;
    }
  else
    {
      itsFrameCount++;

      CodecFlags ignore;

      // decompress a frame into the window - can queue a frame for async decompression when passed in a completion proc
      if (noErr != DecompressSequenceFrameS
          (itsDrawSeq, // sequence ID returned by DecompressSequenceBegin
           p,          // pointer to compressed image data
           len,        // size of the buffer
           0,          // in flags
           &ignore,    // out flags
           NULL))      // async completion proc
        {
          itsErrorMsg =  "DSeqFrameS failed";
          return OSErr(-1);
        }

      // get the information we need from the GWorld
      Rect pbound;
      GetPixBounds(GetGWorldPixMap(itsGWorld), &pbound);

      char* const baseAddr =
        GetPixBaseAddr(GetGWorldPixMap(itsGWorld));

      const long rowBytes =
        QTGetPixMapHandleRowBytes(GetGWorldPixMap(itsGWorld));

      itsCurrentImage.resize(Dims(pbound.right - pbound.left,
                                     pbound.bottom - pbound.top));

      Image<PixRGB<byte> >::iterator itr = itsCurrentImage.beginw();

      for (int y = pbound.top; y < pbound.bottom; ++y)
        {
          char* p = baseAddr + rowBytes * (y-pbound.top);

          for (int x = pbound.left; x < pbound.right; ++x)
            {
              const UInt32 color = *((UInt32*)(p) + x - pbound.left);
              const UInt32 R = (color & 0x00FF0000) >> 16;
              const UInt32 G = (color & 0x0000FF00) >> 8;
              const UInt32 B = (color & 0x000000FF) >> 0;

              *itr++ = PixRGB<byte>(R,G,B);
            }
        }

      itsSkipFrameCount = 0;
      itsPrevTime = time;
      itsGotFrame = true;
    }

  // status information
  const float fps = (float)itsTimeScale / (float)frameTimeDelta;
  const float averagefps = ((float)itsFrameCount * (float)itsTimeScale) / (float)time;
  const UInt8 minutes = (time / itsTimeScale) / 60;
  const UInt8 seconds = (time / itsTimeScale) % 60;
  const UInt8 frames = (time % itsTimeScale) / frameTimeDelta;
  LDEBUG("#%06ld t:%ld nq:%u, %02d:%02d.%02d, fps:%5.1f av:%5.1f",
         itsFrameCount, time, itsQueuedFrameCount,
         minutes, seconds, frames, fps, averagefps);

  return noErr;
}