// Closes the instance of the component.
// Release all storage associated with this instance.
// Note that if a component's Open function fails with an error, its Close function will still be called.
ComponentResult
VP8_Encoder_Close(
                  VP8EncoderGlobals glob,
                  ComponentInstance self)
{
  dbg_printf("[vp8e - %08lx]  Close Called\n", (UInt32)glob);

  if (glob)
  {
    if (glob->stats.buf != NULL)
    {
      free(glob->stats.buf);
      glob->stats.buf =NULL;
      glob->stats.sz=0;
    }

    if (glob->codec) //see if i've initialized the vpx_codec
    {
      if (vpx_codec_destroy(glob->codec))
        dbg_printf("[vp8e - %08lx] Failed to destroy codec\n", (UInt32)glob);

      free(glob->codec);
    }

    ICMCompressionSessionOptionsRelease(glob->sessionOptions);
    glob->sessionOptions = NULL;

    if (glob->raw)
    {
      vpx_img_free(glob->raw);
      free(glob->raw);
    }

    if (glob->sourceQueue.queue != NULL)
      free(glob->sourceQueue.queue);

    free(glob);
  }

  return noErr;
}
示例#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;
}
// Prepare to compress frames.
// Compressor should record session and sessionOptions for use in later calls.
// Compressor may modify imageDescription at this point.
// Compressor may create and return pixel buffer options.
ComponentResult
VP8_Encoder_PrepareToCompressFrames(
                                    VP8EncoderGlobals glob,
                                    ICMCompressorSessionRef session,
                                    ICMCompressionSessionOptionsRef sessionOptions,
                                    ImageDescriptionHandle imageDescription,
                                    void *reserved,
                                    CFDictionaryRef *compressorPixelBufferAttributesOut)
{
  dbg_printf("[vp8e] Prepare to Compress Frames\n", (UInt32)glob);
  ComponentResult err = noErr;
  CFMutableDictionaryRef compressorPixelBufferAttributes = NULL;
  //This format later needs to be converted
  OSType pixelFormatList[] = { k422YpCbCr8PixelFormat }; // also known as '2vuy'

  Fixed gammaLevel;
  int frameIndex;
  SInt32 widthRoundedUp, heightRoundedUp;

  // Record the compressor session for later calls to the ICM.
  // Note: this is NOT a CF type and should NOT be CFRetained or CFReleased.
  glob->session = session;

  // Retain the session options for later use.
  ICMCompressionSessionOptionsRelease(glob->sessionOptions);
  glob->sessionOptions = sessionOptions;
  ICMCompressionSessionOptionsRetain(glob->sessionOptions);

  // Modify imageDescription here if needed.
  // We'll set the image description gamma level to say "2.2".
  gammaLevel = kQTCCIR601VideoGammaLevel;
  err = ICMImageDescriptionSetProperty(imageDescription,
                                       kQTPropertyClass_ImageDescription,
                                       kICMImageDescriptionPropertyID_GammaLevel,
                                       sizeof(gammaLevel),
                                       &gammaLevel);

  if (err)
    goto bail;

  // Record the dimensions from the image description.
  glob->width = (*imageDescription)->width;
  glob->height = (*imageDescription)->height;
  dbg_printf("[vp8e - %08lx] Prepare to compress frame width %d height %d\n", (UInt32)glob, glob->width, glob->height);

  if (glob->width < 16 || glob->width % 2 || glob->height < 16 || glob->height % 2)
    dbg_printf("[vp8e - %08lx] Warning :: Invalid resolution: %ldx%ld", (UInt32)glob, glob->width, glob->height);

  if (glob->raw == NULL)
    glob->raw = calloc(1, sizeof(vpx_image_t));

  //Right now I'm only using YV12, this is great for webm, as I control the spit component
  if (!vpx_img_alloc(glob->raw, IMG_FMT_YV12, glob->width, glob->height, 1))
  {
    dbg_printf("[vp8e - %08lx] Error: Failed to allocate image %dx%d", (UInt32)glob, glob->width, glob->height);
    err = paramErr;
    goto bail;
  }

  glob->maxEncodedDataSize = glob->width * glob->height * 2;
  dbg_printf("[vp8e - %08lx] currently allocating %d bytes as my max encoded size\n", (UInt32)glob, glob->maxEncodedDataSize);

  // Create a pixel buffer attributes dictionary.
  err = createPixelBufferAttributesDictionary(glob->width, glob->height,
                                              pixelFormatList, sizeof(pixelFormatList) / sizeof(OSType),
                                              &compressorPixelBufferAttributes);

  if (err)
    goto bail;

  *compressorPixelBufferAttributesOut = compressorPixelBufferAttributes;
  compressorPixelBufferAttributes = NULL;

  /* Populate encoder configuration */
  glob->res = vpx_codec_enc_config_default((&vpx_codec_vp8_cx_algo), &glob->cfg, 0);

  if (glob->res)
  {
    dbg_printf("[vp8e - %08lx] Failed to get config: %s\n", (UInt32)glob, vpx_codec_err_to_string(glob->res));
    err = paramErr; //this may be something different ....
    goto bail;
  }

  glob->cfg.g_w = glob->width;
  glob->cfg.g_h = glob->height;
  dbg_printf("[vp8e - %08lx] resolution %dx%d\n", (UInt32)glob,
             glob->cfg.g_w, glob->cfg.g_h);

bail:

  if (err)
    dbg_printf("[vp8e - %08lx] Error %d\n", (UInt32)glob, err);

  if (compressorPixelBufferAttributes) CFRelease(compressorPixelBufferAttributes);

  return err;
}