Ejemplo n.º 1
0
static OSStatus setup_video(void)
{
    //Add video track
    videoTrack = NewMovieTrack(movie, video_width << 16, video_height << 16, 0);
    OSStatus theError = GetMoviesError();
    if (theError) {
        log_debug("quicktime_video: error creating movie track");
        return theError;
    }

    //Create video track media
    videoMedia = NewTrackMedia(videoTrack, VideoMediaType, timeScale, 0, 0);
    theError = GetMoviesError();
    if (theError) {
        log_debug("quicktime_video: error creating track media!");
        return theError;
    }

    //Prepare media for editing
    theError = BeginMediaEdits(videoMedia);
    if (theError) {
        log_debug("quicktime_video: error beginning media edits!");
        return theError;
    }

    // ----- Setup Codec -----
    CodecType codec = (CodecType)video_codec;

    // Create compression session
    ICMEncodedFrameOutputRecord record = {
        FrameOutputCallback, NULL, NULL
    };
    theError = ICMCompressionSessionCreate(kCFAllocatorDefault,
                                           video_width, video_height, codec, timeScale, NULL /*options*/, NULL,
                                           &record, &videoCompressionSession);
    if (theError) {
        log_debug("quicktime_video: error creating compression session!");
        return theError;
    }

    // ----- PixelBuffer -----
    theError = CVPixelBufferCreate(NULL, video_width, video_height,
                                   kCVPixelFormatType_24RGB, NULL, &pixelBuffer);
    if (theError) {
        log_debug("quicktime_video: error creating pixel buffer!");
        return theError;
    }
    CVPixelBufferRetain(pixelBuffer);

    video_ready = 1;
    return noErr;
}
Ejemplo n.º 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;
}
Ejemplo n.º 3
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
        {