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
        {
Example #2
0
int qCreateEncoderAPI(QEncoder **eptr, char* encoderArgs, int semaIndex, int width, int height)
{
    QEncoder* encoder;
    OSStatus err;
    QVideoArgs* args = (QVideoArgs*)encoderArgs;

    //XXXX: we should be able to configure this
    SInt32 averageDataRate = 100000;

    ICMEncodedFrameOutputRecord encodedFrameOutputRecord = {0};
    ICMCompressionSessionOptionsRef sessionOptions = NULL;

    CFMutableDictionaryRef pixelBufferAttributes = NULL;
    CFNumberRef number = NULL;
    OSType pixelFormat = Q_PIXEL_FORMAT;

    fprintf(QSTDERR, "\nqCreateEncoderQT(): ABOUT TO TRY TO CREATE ENCODER");
    fprintf(QSTDERR, "\n\t time-scale: %d", args->timeScale);
    fprintf(QSTDERR, "\n\t big-endian: %d", (args->isBigEndian)[0]);
    fprintf(QSTDERR, "\n\t codec-type: %c%c%c%c",
            ((unsigned char*)&(args->codecType))[3],
            ((unsigned char*)&(args->codecType))[2],
            ((unsigned char*)&(args->codecType))[1],
            ((unsigned char*)&(args->codecType))[0]);

    encoder = (QEncoder*)malloc(sizeof(QEncoder));
    if (encoder == NULL) {
        fprintf(QSTDERR, "\nqCreateDecoderQT: failed to malloc encoder struct");
        return -2;
    }

    encoder->semaIndex = semaIndex;
    encoder->timeScale = args->timeScale;
    encoder->codecType = *((CodecType*)(args->codecType));
    encoder->width = width;
    encoder->height = height;

    err = ICMCompressionSessionOptionsCreate( NULL, &sessionOptions );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not create session options");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        free(encoder);
        return -5;
    }

    // Create and configure the compressor component.
    OSType codecManufacturer = 'appl';
    ComponentDescription componentDescription;
    componentDescription.componentType = FOUR_CHAR_CODE('imco');
    componentDescription.componentSubType = encoder->codecType;
    componentDescription.componentManufacturer = codecManufacturer;
    componentDescription.componentFlags = 0;
    componentDescription.componentFlagsMask = 0;

    Component compressorComponent = FindNextComponent(0, &componentDescription);
    if(compressorComponent == NULL) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not find a matching compressor");
        ICMCompressionSessionOptionsRelease(sessionOptions);
        free(encoder);
        return -5;
    }
    err = OpenAComponent(compressorComponent, &(encoder->compressor));
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): failed to open compressor component");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        free(encoder);
        return -5;
    }
    // If we want to use H.264, we need to muck around a bit.
    // XXXX: "clean up" this code.
    if(encoder->codecType == FOUR_CHAR_CODE('avc1'))
    {
        // Profile is currently fixed to Baseline
        // The level is adjusted by the use of the
        // bitrate, but the SPS returned reveals
        // level 1.1 in case of QCIF and level 1.3
        // in case of CIF

        Handle h264Settings = NewHandleClear(0);

        err = ImageCodecGetSettings(encoder->compressor, h264Settings);
        if(err != noErr) {
            fprintf(QSTDERR, "\nqCreateEncoderQT(): failed to get codec settings");
            fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
            ICMCompressionSessionOptionsRelease(sessionOptions);
            CloseComponent(encoder->compressor);
            free(encoder);
            return -5;
        }

        // For some reason, the QTAtomContainer functions will crash if used on the atom
        // container returned by ImageCodecGetSettings.
        // Therefore, we have to parse the atoms self to set the correct settings.
        unsigned i;
        unsigned settingsSize = GetHandleSize(h264Settings) / 4;
        UInt32 *data = (UInt32 *)*h264Settings;
        for(i = 0; i < settingsSize; i++)
        {
            // Forcing Baseline profile
#if defined(__BIG_ENDIAN__)
            if(data[i] == FOUR_CHAR_CODE('sprf'))
            {
                i+=4;
                data[i] = 1;
            }
#else
            if(data[i] == FOUR_CHAR_CODE('frps'))
            {
                i+=4;
                // data[i] = CFSwapInt32(1);
                data[i] = 16777216;  // avoid CFSwapInt32;
            }
#endif

            // if video sent is CIF size, we set this flag to one to have the picture
            // encoded in 5 slices instead of two.
            // If QCIF is sent, this flag remains zero to send two slices instead of
            // one.
#if defined(__BIG_ENDIAN__)
            else if(/*videoSize == XMVideoSize_CIF &&*/ data[i] == FOUR_CHAR_CODE('susg'))
            {
                i+=4;
                data[i] = 1;
            }
#else
            else if(/*videoSize == XMVideoSize_CIF &&*/ data[i] == FOUR_CHAR_CODE('gsus'))
            {
                i+=4;
                // data[i] = CFSwapInt32(1);
                data[i] = 16777216;  // avoid CFSwapInt32;
            }
#endif
        }
        err = ImageCodecSetSettings(encoder->compressor, h264Settings);
        if(err != noErr) {
            fprintf(QSTDERR, "\nqCreateEncoderQT(): failed to set codec settings");
            fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
            ICMCompressionSessionOptionsRelease(sessionOptions);
            CloseComponent(encoder->compressor);
            free(encoder);
            return -5;
        }
    }
    err = ICMCompressionSessionOptionsSetProperty(sessionOptions,
            kQTPropertyClass_ICMCompressionSessionOptions,
            kICMCompressionSessionOptionsPropertyID_CompressorComponent,
            sizeof(encoder->compressor),
            &(encoder->compressor));


    // XXXX: allow (some of) the following options to be set from the 'encoderArgs'

    // We must set this flag to enable P or B frames.
    err = ICMCompressionSessionOptionsSetAllowTemporalCompression( sessionOptions, true );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not enable temporal compression");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    // We must set this flag to enable B frames.
// XXXX:	err = ICMCompressionSessionOptionsSetAllowFrameReordering( sessionOptions, true );
    err = ICMCompressionSessionOptionsSetAllowFrameReordering( sessionOptions, false );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not enable frame reordering");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    // Set the maximum key frame interval, also known as the key frame rate.
    // XXXX: even 5 frames might be a bit long for videoconferencing
    err = ICMCompressionSessionOptionsSetMaxKeyFrameInterval( sessionOptions, 3 );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not set maximum keyframe interval");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    // This allows the compressor more flexibility (ie, dropping and coalescing frames).
    // XXXX: does this mean that playback has to be more careful about when to actually display decoded frames?
// XXXX:	err = ICMCompressionSessionOptionsSetAllowFrameTimeChanges( sessionOptions, true );
    err = ICMCompressionSessionOptionsSetAllowFrameTimeChanges( sessionOptions, false );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not set enable frame time changes");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    // XXXX: CaptureAndCompressIPBMovie set this to true
    err = ICMCompressionSessionOptionsSetDurationsNeeded( sessionOptions, false );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not set whether frame durations are needed");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    // Set the average data rate.
    // XXXX: another good one to parameterize
    // XXXX: can we change this one at runtime?
    err = ICMCompressionSessionOptionsSetProperty(	sessionOptions,
            kQTPropertyClass_ICMCompressionSessionOptions,
            kICMCompressionSessionOptionsPropertyID_AverageDataRate,
            sizeof( averageDataRate ),
            &averageDataRate );
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not set average data rate");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    encodedFrameOutputRecord.encodedFrameOutputCallback = (void*)qQuickTimeEncoderCallback;
    encodedFrameOutputRecord.encodedFrameOutputRefCon = encoder;
    encodedFrameOutputRecord.frameDataAllocator = NULL;

    // Specify attributes for the compression-session's pixel-buffer pool.
    pixelBufferAttributes = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
    number = CFNumberCreate( NULL, kCFNumberIntType, &width );
    CFDictionaryAddValue( pixelBufferAttributes, kCVPixelBufferWidthKey, number );
    CFRelease( number );
    number = CFNumberCreate( NULL, kCFNumberIntType, &height );
    CFDictionaryAddValue( pixelBufferAttributes, kCVPixelBufferHeightKey, number );
    CFRelease( number );
    number = CFNumberCreate( NULL, kCFNumberSInt32Type, &pixelFormat );
    CFDictionaryAddValue( pixelBufferAttributes, kCVPixelBufferPixelFormatTypeKey, number );
    CFRelease( number );

    err = ICMCompressionSessionCreate(	NULL,
                                        width,
                                        height,
                                        args->codecType,
                                        args->timeScale,
                                        sessionOptions,
                                        pixelBufferAttributes,
                                        &encodedFrameOutputRecord,
                                        &(encoder->session));
    CFRelease(pixelBufferAttributes);
    if(err != noErr) {
        fprintf(QSTDERR, "\nqCreateEncoderQT(): could not create compression session");
        fprintf(QSTDERR, "\n\tQUICKTIME ERROR CODE: %d", err);
        ICMCompressionSessionOptionsRelease(sessionOptions);
        CloseComponent(encoder->compressor);
        free(encoder);
        return -5;
    }

    ICMCompressionSessionOptionsRelease(sessionOptions);

    *eptr = encoder;
    return 0;
}
Example #3
0
static ComponentResult _setup_cs(StreamInfoPtr si)
{
    ComponentResult err = noErr;
    ICMEncodedFrameOutputRecord efor;
    SInt32 tmpval = 0;

    dbg_printf("[ vOE]  >> [%08lx] :: _setup_cs()\n", (UInt32) -1);

    if (si->si_v.cs) {
        ICMCompressionSessionCompleteFrames(si->si_v.cs, true, 0, 0);
        ICMCompressionSessionRelease(si->si_v.cs);
        si->si_v.cs = NULL;
    }

    err = ICMCompressionSessionOptionsCreate(NULL, &si->si_v.cs_opts);
    if (err)
        goto bail;


    // We must set this flag to enable P or B frames.
    err = ICMCompressionSessionOptionsSetAllowTemporalCompression(si->si_v.cs_opts,
                                                                  true);
    dbg_printf("[ vOE]  ?1 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    // We must set this flag to enable B frames.
    err = ICMCompressionSessionOptionsSetAllowFrameReordering(si->si_v.cs_opts,
                                                              true);
    dbg_printf("[ vOE]  ?2 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    // Set the maximum key frame rate.
    err = ICMCompressionSessionOptionsSetMaxKeyFrameInterval(si->si_v.cs_opts,
                                                             si->si_v.keyrate);
    dbg_printf("[ vOE]  ?3 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    // This allows the compressor more flexibility
    //  (ie, dropping and coalescing frames).
    err = ICMCompressionSessionOptionsSetAllowFrameTimeChanges(si->si_v.cs_opts,
                                                               true);
    dbg_printf("[ vOE]  ?4 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    // We need durations when we store frames.
    err = ICMCompressionSessionOptionsSetDurationsNeeded(si->si_v.cs_opts, true);
    dbg_printf("[ vOE]  ?5 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    tmpval = si->si_v.quality;
    err = ICMCompressionSessionOptionsSetProperty(si->si_v.cs_opts,
                                                  kQTPropertyClass_ICMCompressionSessionOptions,
                                                  kICMCompressionSessionOptionsPropertyID_Quality,
                                                  sizeof(tmpval),
                                                  &tmpval);
    dbg_printf("[ vOE]  ?6 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    err = ICMCompressionSessionOptionsSetProperty(si->si_v.cs_opts,
                                                  kQTPropertyClass_ICMCompressionSessionOptions,
                                                  kICMCompressionSessionOptionsPropertyID_ExpectedFrameRate,
                                                  sizeof(si->si_v.fps),
                                                  &si->si_v.fps);
    dbg_printf("[ vOE]  ?7 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    tmpval = si->si_v.bitrate;
    err = ICMCompressionSessionOptionsSetProperty(si->si_v.cs_opts,
                                                  kQTPropertyClass_ICMCompressionSessionOptions,
                                                  kICMCompressionSessionOptionsPropertyID_AverageDataRate,
                                                  sizeof(tmpval),
                                                  &tmpval);
    dbg_printf("[ vOE]  ?8 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
    if (err)
        goto bail;

    if (si->si_v.custom != NULL) {
        err = ICMCompressionSessionOptionsSetProperty(si->si_v.cs_opts,
                                                      kQTPropertyClass_ICMCompressionSessionOptions,
                                                      kICMCompressionSessionOptionsPropertyID_CompressorSettings,
                                                      sizeof(si->si_v.custom),
                                                      &si->si_v.custom);
        dbg_printf("[ vOE]  ?9 [%08lx] :: _setup_cs() = %ld\n", (UInt32) -1, err);
        if (err)
            goto bail;
    }

    if (!err) {
        efor.encodedFrameOutputCallback = _frame_compressed;
        efor.encodedFrameOutputRefCon = (void *) si;
        efor.frameDataAllocator = NULL;

        err = ICMCompressionSessionCreate(NULL, si->si_v.width >> 16,
                                          si->si_v.height >> 16,
                                          kVideoFormatXiphTheora, /* fixed for now... */
                                          si->sourceTimeScale, si->si_v.cs_opts,
                                          NULL, &efor, &si->si_v.cs);
        dbg_printf("[ vOE]  ?A [%08lx] :: _setup_cs() = %ld [%lx x %lx]\n",
                   (UInt32) -1, err, si->si_v.width, si->si_v.height);
    }