]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/videotoolboxenc.c
Merge commit 'dbbaad32e36b9046cd19d6e3329e2ca7ab49b401'
[ffmpeg] / libavcodec / videotoolboxenc.c
index 57ea2b336fdc1ed7e25242ff7ee57b45163973a2..4345ca37b240af5261418f67ab243fbff0cba181 100644 (file)
@@ -96,6 +96,13 @@ typedef struct VTEncContext {
     bool warned_color_range;
 } VTEncContext;
 
+static int vtenc_populate_extradata(AVCodecContext   *avctx,
+                                    CMVideoCodecType codec_type,
+                                    CFStringRef      profile_level,
+                                    CFNumberRef      gamma_level,
+                                    CFDictionaryRef  enc_info,
+                                    CFDictionaryRef  pixel_buffer_info);
+
 /**
  * NULL-safe release of *refPtr, and sets value to NULL.
  */
@@ -199,6 +206,45 @@ static void vtenc_q_push(VTEncContext *vtctx, CMSampleBufferRef buffer)
     pthread_mutex_unlock(&vtctx->lock);
 }
 
+static int count_nalus(size_t length_code_size,
+                       CMSampleBufferRef sample_buffer,
+                       int *count)
+{
+    size_t offset = 0;
+    int status;
+    int nalu_ct = 0;
+    uint8_t size_buf[4];
+    size_t src_size = CMSampleBufferGetTotalSampleSize(sample_buffer);
+    CMBlockBufferRef block = CMSampleBufferGetDataBuffer(sample_buffer);
+
+    if (length_code_size > 4)
+        return AVERROR_INVALIDDATA;
+
+    while (offset < src_size) {
+        size_t curr_src_len;
+        size_t box_len = 0;
+        size_t i;
+
+        status = CMBlockBufferCopyDataBytes(block,
+                                            offset,
+                                            length_code_size,
+                                            size_buf);
+
+        for (i = 0; i < length_code_size; i++) {
+            box_len <<= 8;
+            box_len |= size_buf[i];
+        }
+
+        curr_src_len = box_len + length_code_size;
+        offset += curr_src_len;
+
+        nalu_ct++;
+    }
+
+    *count = nalu_ct;
+    return 0;
+}
+
 static CMVideoCodecType get_cm_codec_type(enum AVCodecID id)
 {
     switch (id) {
@@ -207,12 +253,6 @@ static CMVideoCodecType get_cm_codec_type(enum AVCodecID id)
     }
 }
 
-static void vtenc_free_block(void *opaque, uint8_t *data)
-{
-    CMBlockBufferRef block = opaque;
-    CFRelease(block);
-}
-
 /**
  * Get the parameter sets from a CMSampleBufferRef.
  * @param dst If *dst isn't NULL, the parameters are copied into existing
@@ -355,7 +395,7 @@ static int set_extradata(AVCodecContext *avctx, CMSampleBufferRef sample_buffer)
         return status;
     }
 
-    avctx->extradata = av_malloc(total_size);
+    avctx->extradata = av_mallocz(total_size + AV_INPUT_BUFFER_PADDING_SIZE);
     if (!avctx->extradata) {
         return AVERROR(ENOMEM);
     }
@@ -728,83 +768,28 @@ static int get_cv_ycbcr_matrix(AVCodecContext *avctx, CFStringRef *matrix) {
     return 0;
 }
 
-
-static av_cold int vtenc_init(AVCodecContext *avctx)
+static int vtenc_create_encoder(AVCodecContext   *avctx,
+                                CMVideoCodecType codec_type,
+                                CFStringRef      profile_level,
+                                CFNumberRef      gamma_level,
+                                CFDictionaryRef  enc_info,
+                                CFDictionaryRef  pixel_buffer_info,
+                                VTCompressionSessionRef *session)
 {
-    CFMutableDictionaryRef enc_info;
-    CFMutableDictionaryRef pixel_buffer_info;
-    CMVideoCodecType       codec_type;
-    VTEncContext           *vtctx = avctx->priv_data;
-    CFStringRef            profile_level;
-    SInt32                 bit_rate = avctx->bit_rate;
-    CFNumberRef            bit_rate_num;
-    CFBooleanRef           has_b_frames_cfbool;
-    CFNumberRef            gamma_level;
-    int                    status;
-
-    codec_type = get_cm_codec_type(avctx->codec_id);
-    if (!codec_type) {
-        av_log(avctx, AV_LOG_ERROR, "Error: no mapping for AVCodecID %d\n", avctx->codec_id);
-        return AVERROR(EINVAL);
-    }
-
-    vtctx->has_b_frames = avctx->max_b_frames > 0;
-    if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){
-        av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n");
-        vtctx->has_b_frames = false;
-    }
-
-    if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) {
-        av_log(avctx, AV_LOG_WARNING, "CABAC entropy requires 'main' or 'high' profile, but baseline was requested. Encode will not use CABAC entropy.\n");
-        vtctx->entropy = VT_ENTROPY_NOT_SET;
-    }
-
-    if (!get_vt_profile_level(avctx, &profile_level)) return AVERROR(EINVAL);
-
-    vtctx->session = NULL;
-
-    enc_info = CFDictionaryCreateMutable(
-        kCFAllocatorDefault,
-        20,
-        &kCFCopyStringDictionaryKeyCallBacks,
-        &kCFTypeDictionaryValueCallBacks
-    );
-
-    if (!enc_info) return AVERROR(ENOMEM);
-
-#if !TARGET_OS_IPHONE
-    if (!vtctx->allow_sw) {
-        CFDictionarySetValue(enc_info, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
-    } else {
-        CFDictionarySetValue(enc_info, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,  kCFBooleanTrue);
-    }
-#endif
-
-    if (avctx->pix_fmt != AV_PIX_FMT_VIDEOTOOLBOX) {
-        status = create_cv_pixel_buffer_info(avctx, &pixel_buffer_info);
-        if (status) {
-            CFRelease(enc_info);
-            return status;
-        }
-    } else {
-        pixel_buffer_info = NULL;
-    }
-
-    status = VTCompressionSessionCreate(
-        kCFAllocatorDefault,
-        avctx->width,
-        avctx->height,
-        codec_type,
-        enc_info,
-        pixel_buffer_info,
-        kCFAllocatorDefault,
-        vtenc_output_callback,
-        avctx,
-        &vtctx->session
-    );
-
-    if (pixel_buffer_info) CFRelease(pixel_buffer_info);
-    CFRelease(enc_info);
+    VTEncContext *vtctx = avctx->priv_data;
+    SInt32       bit_rate = avctx->bit_rate;
+    CFNumberRef  bit_rate_num;
+
+    int status = VTCompressionSessionCreate(kCFAllocatorDefault,
+                                            avctx->width,
+                                            avctx->height,
+                                            codec_type,
+                                            enc_info,
+                                            pixel_buffer_info,
+                                            kCFAllocatorDefault,
+                                            vtenc_output_callback,
+                                            avctx,
+                                            session);
 
     if (status || !vtctx->session) {
         av_log(avctx, AV_LOG_ERROR, "Error: cannot create compression session: %d\n", status);
@@ -949,8 +934,8 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
         }
     }
 
-    status = get_cv_transfer_function(avctx, &vtctx->transfer_function, &gamma_level);
-    if (!status && vtctx->transfer_function) {
+
+    if (vtctx->transfer_function) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_TransferFunction,
                                       vtctx->transfer_function);
@@ -960,8 +945,8 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
         }
     }
 
-    status = get_cv_ycbcr_matrix(avctx, &vtctx->ycbcr_matrix);
-    if (!status && vtctx->ycbcr_matrix) {
+
+    if (vtctx->ycbcr_matrix) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_YCbCrMatrix,
                                       vtctx->ycbcr_matrix);
@@ -971,8 +956,8 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
         }
     }
 
-    status = get_cv_color_primaries(avctx, &vtctx->color_primaries);
-    if (!status && vtctx->color_primaries) {
+
+    if (vtctx->color_primaries) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_ColorPrimaries,
                                       vtctx->color_primaries);
@@ -982,7 +967,7 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
         }
     }
 
-    if (!status && gamma_level) {
+    if (gamma_level) {
         status = VTSessionSetProperty(vtctx->session,
                                       kCVImageBufferGammaLevelKey,
                                       gamma_level);
@@ -1034,10 +1019,97 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
         return AVERROR_EXTERNAL;
     }
 
+    return 0;
+}
+
+static av_cold int vtenc_init(AVCodecContext *avctx)
+{
+    CFMutableDictionaryRef enc_info;
+    CFMutableDictionaryRef pixel_buffer_info;
+    CMVideoCodecType       codec_type;
+    VTEncContext           *vtctx = avctx->priv_data;
+    CFStringRef            profile_level;
+    CFBooleanRef           has_b_frames_cfbool;
+    CFNumberRef            gamma_level = NULL;
+    int                    status;
+
+    codec_type = get_cm_codec_type(avctx->codec_id);
+    if (!codec_type) {
+        av_log(avctx, AV_LOG_ERROR, "Error: no mapping for AVCodecID %d\n", avctx->codec_id);
+        return AVERROR(EINVAL);
+    }
+
+    vtctx->has_b_frames = avctx->max_b_frames > 0;
+    if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){
+        av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n");
+        vtctx->has_b_frames = false;
+    }
+
+    if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) {
+        av_log(avctx, AV_LOG_WARNING, "CABAC entropy requires 'main' or 'high' profile, but baseline was requested. Encode will not use CABAC entropy.\n");
+        vtctx->entropy = VT_ENTROPY_NOT_SET;
+    }
+
+    if (!get_vt_profile_level(avctx, &profile_level)) return AVERROR(EINVAL);
+
+    vtctx->session = NULL;
+
+    enc_info = CFDictionaryCreateMutable(
+        kCFAllocatorDefault,
+        20,
+        &kCFCopyStringDictionaryKeyCallBacks,
+        &kCFTypeDictionaryValueCallBacks
+    );
+
+    if (!enc_info) return AVERROR(ENOMEM);
+
+#if !TARGET_OS_IPHONE
+    if (!vtctx->allow_sw) {
+        CFDictionarySetValue(enc_info, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
+    } else {
+        CFDictionarySetValue(enc_info, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,  kCFBooleanTrue);
+    }
+#endif
+
+    if (avctx->pix_fmt != AV_PIX_FMT_VIDEOTOOLBOX) {
+        status = create_cv_pixel_buffer_info(avctx, &pixel_buffer_info);
+        if (status)
+            goto init_cleanup;
+    } else {
+        pixel_buffer_info = NULL;
+    }
+
     pthread_mutex_init(&vtctx->lock, NULL);
     pthread_cond_init(&vtctx->cv_sample_sent, NULL);
     vtctx->dts_delta = vtctx->has_b_frames ? -1 : 0;
 
+    get_cv_transfer_function(avctx, &vtctx->transfer_function, &gamma_level);
+    get_cv_ycbcr_matrix(avctx, &vtctx->ycbcr_matrix);
+    get_cv_color_primaries(avctx, &vtctx->color_primaries);
+
+
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+        status = vtenc_populate_extradata(avctx,
+                                          codec_type,
+                                          profile_level,
+                                          gamma_level,
+                                          enc_info,
+                                          pixel_buffer_info);
+        if (status)
+            goto init_cleanup;
+    }
+
+    status = vtenc_create_encoder(avctx,
+                                  codec_type,
+                                  profile_level,
+                                  gamma_level,
+                                  enc_info,
+                                  pixel_buffer_info,
+                                  &vtctx->session);
+
+    if (status < 0)
+        goto init_cleanup;
+
     status = VTSessionCopyProperty(vtctx->session,
                                    kVTCompressionPropertyKey_AllowFrameReordering,
                                    kCFAllocatorDefault,
@@ -1050,7 +1122,16 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
     }
     avctx->has_b_frames = vtctx->has_b_frames;
 
-    return 0;
+init_cleanup:
+    if (gamma_level)
+        CFRelease(gamma_level);
+
+    if (pixel_buffer_info)
+        CFRelease(pixel_buffer_info);
+
+    CFRelease(enc_info);
+
+    return status;
 }
 
 static void vtenc_get_frame_info(CMSampleBufferRef buffer, bool *is_key_frame)
@@ -1080,54 +1161,6 @@ static void vtenc_get_frame_info(CMSampleBufferRef buffer, bool *is_key_frame)
     }
 }
 
-/**
- * Replaces length codes with H.264 Annex B start codes.
- * length_code_size must equal sizeof(start_code).
- * On failure, the contents of data may have been modified.
- *
- * @param length_code_size Byte length of each length code
- * @param data Call with NAL units prefixed with length codes.
- *             On success, the length codes are replace with
- *             start codes.
- * @param size Length of data, excluding any padding.
- * @return 0 on success
- *         AVERROR_BUFFER_TOO_SMALL if length code size is smaller
- *         than a start code or if a length_code in data specifies
- *         data beyond the end of its buffer.
- */
-static int replace_length_codes(size_t  length_code_size,
-                                uint8_t *data,
-                                size_t  size)
-{
-    size_t remaining_size = size;
-
-    if (length_code_size != sizeof(start_code)) {
-        av_log(NULL, AV_LOG_ERROR, "Start code size and length code size not equal.\n");
-        return AVERROR_BUFFER_TOO_SMALL;
-    }
-
-    while (remaining_size > 0) {
-        size_t box_len = 0;
-        size_t i;
-
-        for (i = 0; i < length_code_size; i++) {
-            box_len <<= 8;
-            box_len |= data[i];
-        }
-
-        if (remaining_size < box_len + sizeof(start_code)) {
-            av_log(NULL, AV_LOG_ERROR, "Length is out of range.\n");
-            AVERROR_BUFFER_TOO_SMALL;
-        }
-
-        memcpy(data, start_code, sizeof(start_code));
-        data += box_len + sizeof(start_code);
-        remaining_size -= box_len + sizeof(start_code);
-    }
-
-    return 0;
-}
-
 /**
  * Copies NAL units and replaces length codes with
  * H.264 Annex B start codes. On failure, the contents of
@@ -1148,14 +1181,19 @@ static int replace_length_codes(size_t  length_code_size,
  *         the end of its buffer.
  */
 static int copy_replace_length_codes(
+    AVCodecContext *avctx,
     size_t        length_code_size,
-    const uint8_t *src_data,
-    size_t        src_size,
+    CMSampleBufferRef sample_buffer,
     uint8_t       *dst_data,
     size_t        dst_size)
 {
+    size_t src_size = CMSampleBufferGetTotalSampleSize(sample_buffer);
     size_t remaining_src_size = src_size;
     size_t remaining_dst_size = dst_size;
+    size_t src_offset = 0;
+    int status;
+    uint8_t size_buf[4];
+    CMBlockBufferRef block = CMSampleBufferGetDataBuffer(sample_buffer);
 
     if (length_code_size > 4) {
         return AVERROR_INVALIDDATA;
@@ -1168,11 +1206,19 @@ static int copy_replace_length_codes(
         size_t i;
 
         uint8_t       *dst_box;
-        const uint8_t *src_box;
+
+        status = CMBlockBufferCopyDataBytes(block,
+                                            src_offset,
+                                            length_code_size,
+                                            size_buf);
+        if (status) {
+            av_log(avctx, AV_LOG_ERROR, "Cannot copy length: %d\n", status);
+            return AVERROR_EXTERNAL;
+        }
 
         for (i = 0; i < length_code_size; i++) {
             box_len <<= 8;
-            box_len |= src_data[i];
+            box_len |= size_buf[i];
         }
 
         curr_src_len = box_len + length_code_size;
@@ -1187,12 +1233,19 @@ static int copy_replace_length_codes(
         }
 
         dst_box = dst_data + sizeof(start_code);
-        src_box = src_data + length_code_size;
 
         memcpy(dst_data, start_code, sizeof(start_code));
-        memcpy(dst_box,  src_box,    box_len);
+        status = CMBlockBufferCopyDataBytes(block,
+                                            src_offset + length_code_size,
+                                            box_len,
+                                            dst_box);
+
+        if (status) {
+            av_log(avctx, AV_LOG_ERROR, "Cannot copy data: %d\n", status);
+            return AVERROR_EXTERNAL;
+        }
 
-        src_data += curr_src_len;
+        src_offset += curr_src_len;
         dst_data += curr_dst_len;
 
         remaining_src_size -= curr_src_len;
@@ -1212,16 +1265,15 @@ static int vtenc_cm_to_avpacket(
     int     status;
     bool    is_key_frame;
     bool    add_header;
-    char    *buf_data;
     size_t  length_code_size;
     size_t  header_size = 0;
     size_t  in_buf_size;
+    size_t  out_buf_size;
     int64_t dts_delta;
     int64_t time_base_num;
+    int nalu_count;
     CMTime  pts;
     CMTime  dts;
-
-    CMBlockBufferRef            block;
     CMVideoFormatDescriptionRef vid_fmt;
 
 
@@ -1235,82 +1287,42 @@ static int vtenc_cm_to_avpacket(
         vid_fmt = CMSampleBufferGetFormatDescription(sample_buffer);
         if (!vid_fmt) {
             av_log(avctx, AV_LOG_ERROR, "Cannot get format description.\n");
+            return AVERROR_EXTERNAL;
         }
 
         int status = get_params_size(avctx, vid_fmt, &header_size);
         if (status) return status;
     }
 
-    block = CMSampleBufferGetDataBuffer(sample_buffer);
-    if (!block) {
-        av_log(avctx, AV_LOG_ERROR, "Could not get block buffer from sample buffer.\n");
-        return AVERROR_EXTERNAL;
-    }
-
-
-    status = CMBlockBufferGetDataPointer(block, 0, &in_buf_size, NULL, &buf_data);
-    if (status) {
-        av_log(avctx, AV_LOG_ERROR, "Error: cannot get data pointer: %d\n", status);
-        return AVERROR_EXTERNAL;
-    }
-
-    size_t out_buf_size = header_size + in_buf_size;
-    bool can_reuse_cmbuffer = !add_header &&
-                              !pkt->data  &&
-                              length_code_size == sizeof(start_code);
-
-    av_init_packet(pkt);
-
-    if (can_reuse_cmbuffer) {
-        AVBufferRef* buf_ref = av_buffer_create(
-            buf_data,
-            out_buf_size,
-            vtenc_free_block,
-            block,
-            0
-        );
-
-        if (!buf_ref) return AVERROR(ENOMEM);
-
-        CFRetain(block);
+    status = count_nalus(length_code_size, sample_buffer, &nalu_count);
+    if(status)
+        return status;
 
-        pkt->buf  = buf_ref;
-        pkt->data = buf_data;
-        pkt->size = in_buf_size;
+    in_buf_size = CMSampleBufferGetTotalSampleSize(sample_buffer);
+    out_buf_size = header_size +
+                   in_buf_size +
+                   nalu_count * ((int)sizeof(start_code) - (int)length_code_size);
 
-        status = replace_length_codes(length_code_size, pkt->data, pkt->size);
-        if (status) {
-            av_log(avctx, AV_LOG_ERROR, "Error replacing length codes: %d\n", status);
-            return status;
-        }
-    } else {
-        if (!pkt->data) {
-            status = av_new_packet(pkt, out_buf_size);
-            if(status) return status;
-        }
-
-        if (pkt->size < out_buf_size) {
-            av_log(avctx, AV_LOG_ERROR, "Error: packet's buffer is too small.\n");
-            return AVERROR_BUFFER_TOO_SMALL;
-        }
+    status = ff_alloc_packet2(avctx, pkt, out_buf_size, out_buf_size);
+    if (status < 0)
+        return status;
 
-        if (add_header) {
-            status = copy_param_sets(avctx, vid_fmt, pkt->data, out_buf_size);
-            if(status) return status;
-        }
+    if (add_header) {
+        status = copy_param_sets(avctx, vid_fmt, pkt->data, out_buf_size);
+        if(status) return status;
+    }
 
-        status = copy_replace_length_codes(
-            length_code_size,
-            buf_data,
-            in_buf_size,
-            pkt->data + header_size,
-            pkt->size - header_size
-        );
+    status = copy_replace_length_codes(
+        avctx,
+        length_code_size,
+        sample_buffer,
+        pkt->data + header_size,
+        pkt->size - header_size
+    );
 
-        if (status) {
-            av_log(avctx, AV_LOG_ERROR, "Error copying packet data: %d", status);
-            return status;
-        }
+    if (status) {
+        av_log(avctx, AV_LOG_ERROR, "Error copying packet data: %d", status);
+        return status;
     }
 
     if (is_key_frame) {
@@ -1333,6 +1345,7 @@ static int vtenc_cm_to_avpacket(
     time_base_num = avctx->time_base.num;
     pkt->pts = pts.value / time_base_num;
     pkt->dts = dts.value / time_base_num - dts_delta;
+    pkt->size = out_buf_size;
 
     return 0;
 }
@@ -1788,6 +1801,108 @@ end_nopkt:
     return status;
 }
 
+static int vtenc_populate_extradata(AVCodecContext   *avctx,
+                                    CMVideoCodecType codec_type,
+                                    CFStringRef      profile_level,
+                                    CFNumberRef      gamma_level,
+                                    CFDictionaryRef  enc_info,
+                                    CFDictionaryRef  pixel_buffer_info)
+{
+    VTEncContext *vtctx = avctx->priv_data;
+    AVFrame *frame = av_frame_alloc();
+    int y_size = avctx->width * avctx->height;
+    int chroma_size = (avctx->width / 2) * (avctx->height / 2);
+    CMSampleBufferRef buf = NULL;
+    int status;
+
+    if (!frame)
+        return AVERROR(ENOMEM);
+
+    frame->buf[0] = av_buffer_alloc(y_size + 2 * chroma_size);
+
+    if(!frame->buf[0]){
+        status = AVERROR(ENOMEM);
+        goto pe_cleanup;
+    }
+
+    status = vtenc_create_encoder(avctx,
+                                  codec_type,
+                                  profile_level,
+                                  gamma_level,
+                                  enc_info,
+                                  pixel_buffer_info,
+                                  &vtctx->session);
+    if (status)
+        goto pe_cleanup;
+
+    frame->data[0] = frame->buf[0]->data;
+    memset(frame->data[0],   0,      y_size);
+
+    frame->data[1] = frame->buf[0]->data + y_size;
+    memset(frame->data[1], 128, chroma_size);
+
+
+    if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) {
+        frame->data[2] = frame->buf[0]->data + y_size + chroma_size;
+        memset(frame->data[2], 128, chroma_size);
+    }
+
+    frame->linesize[0] = avctx->width;
+
+    if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) {
+        frame->linesize[1] =
+        frame->linesize[2] = (avctx->width + 1) / 2;
+    } else {
+        frame->linesize[1] = (avctx->width + 1) / 2;
+    }
+
+    frame->format          = avctx->pix_fmt;
+    frame->width           = avctx->width;
+    frame->height          = avctx->height;
+    av_frame_set_colorspace(frame, avctx->colorspace);
+    av_frame_set_color_range(frame, avctx->color_range);
+    frame->color_trc       = avctx->color_trc;
+    frame->color_primaries = avctx->color_primaries;
+
+    frame->pts = 0;
+    status = vtenc_send_frame(avctx, vtctx, frame);
+    if (status) {
+        av_log(avctx, AV_LOG_ERROR, "Error sending frame: %d\n", status);
+        goto pe_cleanup;
+    }
+
+    //Populates extradata - output frames are flushed and param sets are available.
+    status = VTCompressionSessionCompleteFrames(vtctx->session,
+                                                kCMTimeIndefinite);
+
+    if (status)
+        goto pe_cleanup;
+
+    status = vtenc_q_pop(vtctx, 0, &buf);
+    if (status) {
+        av_log(avctx, AV_LOG_ERROR, "popping: %d\n", status);
+        goto pe_cleanup;
+    }
+
+    CFRelease(buf);
+
+
+
+pe_cleanup:
+    if(vtctx->session)
+        CFRelease(vtctx->session);
+
+    vtctx->session = NULL;
+    vtctx->frame_ct_out = 0;
+
+    av_frame_unref(frame);
+    av_frame_free(&frame);
+
+    av_assert0(status != 0 || (avctx->extradata && avctx->extradata_size > 0));
+
+    return status;
+}
+
 static av_cold int vtenc_close(AVCodecContext *avctx)
 {
     VTEncContext *vtctx = avctx->priv_data;