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; }
bool UBQuickTimeFile::createCompressionSession() { CodecType codecType = kH264CodecType; CFStringRef keys[] = {kCVPixelBufferPixelFormatTypeKey, kCVPixelBufferWidthKey, kCVPixelBufferHeightKey}; int width = mFrameSize.width(); int height = mFrameSize.height(); int pixelFormat = k32BGRAPixelFormat; CFTypeRef values[] = { (CFTypeRef)CFNumberCreate(0, kCFNumberIntType, (void*)&pixelFormat), (CFTypeRef)CFNumberCreate(0, kCFNumberIntType, (void*)&width), (CFTypeRef)CFNumberCreate(0, kCFNumberIntType, (void*)&height) }; CFDictionaryRef pixelBufferAttributes = CFDictionaryCreate(kCFAllocatorDefault , (const void **)keys, (const void **)values, 3, 0, 0); if(!pixelBufferAttributes) { setLastErrorMessage("Could not create CV buffer pool pixel buffer attributes"); return false; } OSStatus err = noErr; ICMEncodedFrameOutputRecord encodedFrameOutputRecord = {0}; ICMCompressionSessionOptionsRef sessionOptions = 0; err = ICMCompressionSessionOptionsCreate(0, &sessionOptions); if(err) { setLastErrorMessage(QString("ICMCompressionSessionOptionsCreate() failed %1").arg(err)); goto bail; } // We must set this flag to enable P or B frames. err = ICMCompressionSessionOptionsSetAllowTemporalCompression(sessionOptions, true); if(err) { setLastErrorMessage(QString("ICMCompressionSessionOptionsSetAllowTemporalCompression() failed %1").arg(err)); goto bail; } // We must set this flag to enable B frames. err = ICMCompressionSessionOptionsSetAllowFrameReordering(sessionOptions, true); if(err) { setLastErrorMessage(QString("ICMCompressionSessionOptionsSetAllowFrameReordering() failed %1").arg(err)); goto bail; } // Set the maximum key frame interval, also known as the key frame rate. err = ICMCompressionSessionOptionsSetMaxKeyFrameInterval(sessionOptions, mFramesPerSecond); if(err) { setLastErrorMessage(QString("ICMCompressionSessionOptionsSetMaxKeyFrameInterval() failed %1").arg(err)); goto bail; } // This allows the compressor more flexibility (ie, dropping and coalescing frames). err = ICMCompressionSessionOptionsSetAllowFrameTimeChanges(sessionOptions, true); if(err) { setLastErrorMessage(QString("ICMCompressionSessionOptionsSetAllowFrameTimeChanges() failed %1").arg(err)); goto bail; } // Set the average quality. err = ICMCompressionSessionOptionsSetProperty(sessionOptions, kQTPropertyClass_ICMCompressionSessionOptions, kICMCompressionSessionOptionsPropertyID_Quality, sizeof(mSpatialQuality), &mSpatialQuality); if(err) { setLastErrorMessage(QString("ICMCompressionSessionOptionsSetProperty(Quality) failed %1").arg(err)); goto bail; } //qDebug() << "available quality" << mSpatialQuality; encodedFrameOutputRecord.encodedFrameOutputCallback = addEncodedFrameToMovie; encodedFrameOutputRecord.encodedFrameOutputRefCon = this; encodedFrameOutputRecord.frameDataAllocator = 0; err = ICMCompressionSessionCreate(0, mFrameSize.width(), mFrameSize.height(), codecType, mTimeScale, sessionOptions, pixelBufferAttributes, &encodedFrameOutputRecord, &mVideoCompressionSession); if(err) { setLastErrorMessage(QString("ICMCompressionSessionCreate() failed %1").arg(err)); goto bail; } mCVPixelBufferPool = ICMCompressionSessionGetPixelBufferPool(mVideoCompressionSession); if(!mCVPixelBufferPool) { setLastErrorMessage("ICMCompressionSessionGetPixelBufferPool() failed."); err = !noErr; goto bail; } if(mRecordAudio) { mWaveRecorder = new UBAudioQueueRecorder(); if(mWaveRecorder->init(mAudioRecordingDeviceName)) { connect(mWaveRecorder, SIGNAL(newWaveBuffer(void*, long, int , const AudioStreamPacketDescription*)) , this, SLOT(appendAudioBuffer(void*, long, int, const AudioStreamPacketDescription*))); connect(mWaveRecorder, SIGNAL(audioLevelChanged(quint8)), this, SIGNAL(audioLevelChanged(quint8))); } else {