int qEncodeAPI(QEncoder* encoder, char* bytes, int byteSize) { OSErr err; CVPixelBufferPoolRef pixelBufferPool; CVPixelBufferRef pixelBuffer; unsigned char* baseAddress; size_t bufferSize; // Grab a pixel buffer from the pool (ICMCompressionSessionEncodeFrame() needs the input // data to be passed in as a CVPixelBufferRef). pixelBufferPool = ICMCompressionSessionGetPixelBufferPool(encoder->session); err = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &pixelBuffer); if (err != noErr) { fprintf(QSTDERR, "\nqEncodeQT(): could not obtain a pixel buffer from pool"); fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err); return -5; } // Lock the pixel-buffer so that we can copy our data into it for encoding // XXXX: would be nice to avoid this copy. err = CVPixelBufferLockBaseAddress(pixelBuffer, 0); if (err != noErr) { fprintf(QSTDERR, "\nqEncodeQT(): could not lock the pixel buffer"); fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err); CVPixelBufferRelease(pixelBuffer); return -5; } baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer); // bufferSize = CVPixelBufferGetWidth(pixelBuffer) * CVPixelBufferGetHeight(pixelBuffer) * 4; bufferSize = CVPixelBufferGetBytesPerRow(pixelBuffer) * CVPixelBufferGetHeight(pixelBuffer); // XXXX: for now, just for debugging. For production, we should notice if this happens and deal with it "appropriately". if (byteSize != bufferSize) { fprintf(QSTDERR, "\nqEncodeQT(): input data size (%d) does not match pixel-buffer data size (%d)", byteSize, bufferSize); } // Copy the data and unlock the buffer memcpy(baseAddress, bytes, bufferSize); CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); // Encode the frame (now in pixel-buffer form). err = ICMCompressionSessionEncodeFrame( encoder->session, pixelBuffer, 0, 0, 0, // we're not specifying a frame time NULL, NULL, NULL); if (err != noErr) { fprintf(QSTDERR, "\nqEncodeQT(): could not encode the frame"); fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err); CVPixelBufferRelease(pixelBuffer); return -5; } CVPixelBufferRelease(pixelBuffer); return 0; }
static void _frame_decompressed(void *decompressionTrackingRefCon, OSStatus err, ICMDecompressionTrackingFlags dtf, CVPixelBufferRef pixelBuffer, TimeValue64 displayTime, TimeValue64 displayDuration, ICMValidTimeFlags validTimeFlags, void *reserved, void *sourceFrameRefCon) { dbg_printf("[ vOE] >> [%08lx] :: _frame_decompressed()\n", (UInt32) -1); if (!err) { StreamInfoPtr si = (StreamInfoPtr) decompressionTrackingRefCon; if (dtf & kICMDecompressionTracking_ReleaseSourceData) { // if we were responsible for managing source data buffers, // we should release the source buffer here, // using sourceFrameRefCon to identify it. } if ((dtf & kICMDecompressionTracking_EmittingFrame) && pixelBuffer) { ICMCompressionFrameOptionsRef frameOptions = NULL; OSType pf = CVPixelBufferGetPixelFormatType(pixelBuffer); dbg_printf("[ vOE] > [%08lx] :: _frame_decompressed() = %ld; %ld," " %lld, %lld, %ld [%ld '%4.4s' (%ld x %ld)]\n", (UInt32) -1, err, dtf, displayTime, displayDuration, validTimeFlags, CVPixelBufferGetDataSize(pixelBuffer), (char *) &pf, CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer)); displayDuration = 25; //? // Feed the frame to the compression session. err = ICMCompressionSessionEncodeFrame(si->si_v.cs, pixelBuffer, displayTime, displayDuration, validTimeFlags, frameOptions, NULL, NULL ); } } dbg_printf("[ vOE] < [%08lx] :: _frame_decompressed() = %ld\n", (UInt32) -1, err); }
static int quicktimedrv_record(screenshot_t *screenshot) { if (!video_ready) { return 0; } OSErr theError; // lock buffer theError = CVPixelBufferLockBaseAddress(pixelBuffer, 0); if (theError) { log_debug("quicktime: error locking pixel buffer!"); return -1; } // fill frame unsigned char *buffer = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer); size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer); unsigned int line_size = screenshot->draw_buffer_line_size; int h = screenshot->height; int w = screenshot->width; int xoff = screenshot->x_offset; int yoff = screenshot->y_offset; BYTE *srcBuffer = screenshot->draw_buffer; // move to last line in tgt buffer and to first in source buffer += (video_yoff) * bytesPerRow + video_xoff * 3; srcBuffer += yoff * line_size + xoff; int x, y; for (y = 0; y < h; y++) { int pix = 0; for (x = 0; x < w; x++) { BYTE val = srcBuffer[x]; buffer[pix++] = screenshot->palette->entries[val].red; buffer[pix++] = screenshot->palette->entries[val].green; buffer[pix++] = screenshot->palette->entries[val].blue; } buffer += bytesPerRow; srcBuffer += line_size; } // unlock buffer theError = CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); if (theError) { log_debug("quicktime: error unlocking pixel buffer!"); return -1; } TimeValue64 next = CVGetCurrentHostTime() / divider; TimeValue64 duration = next - timestamp; timestamp = next; // encode frame theError = ICMCompressionSessionEncodeFrame(videoCompressionSession, pixelBuffer, timestamp, duration, kICMValidTime_DisplayTimeStampIsValid | kICMValidTime_DisplayDurationIsValid, NULL, NULL, (void *)NULL); if (theError) { log_debug("quicktime: error encoding frame!"); return -1; } return 0; }