libavcodec/videotoolboxenc.c
3af71ac3
 /*
  * copyright (c) 2015 Rick Kern <kernrj@gmail.com>
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #include <VideoToolbox/VideoToolbox.h>
 #include <CoreVideo/CoreVideo.h>
 #include <CoreMedia/CoreMedia.h>
 #include <TargetConditionals.h>
 #include <Availability.h>
 #include "avcodec.h"
 #include "libavutil/opt.h"
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavcodec/avcodec.h"
d7cf3610
 #include "libavutil/pixdesc.h"
3af71ac3
 #include "internal.h"
 #include <pthread.h>
9ea91e41
 #include "h264.h"
 #include "h264_sei.h"
dcd3418a
 #include <dlfcn.h>
 
 //These symbols may not be present
 static struct{
     CFStringRef kCVImageBufferColorPrimaries_ITU_R_2020;
     CFStringRef kCVImageBufferTransferFunction_ITU_R_2020;
     CFStringRef kCVImageBufferYCbCrMatrix_ITU_R_2020;
 
     CFStringRef kVTCompressionPropertyKey_H264EntropyMode;
     CFStringRef kVTH264EntropyMode_CAVLC;
     CFStringRef kVTH264EntropyMode_CABAC;
 
     CFStringRef kVTProfileLevel_H264_Baseline_4_0;
     CFStringRef kVTProfileLevel_H264_Baseline_4_2;
     CFStringRef kVTProfileLevel_H264_Baseline_5_0;
     CFStringRef kVTProfileLevel_H264_Baseline_5_1;
     CFStringRef kVTProfileLevel_H264_Baseline_5_2;
     CFStringRef kVTProfileLevel_H264_Baseline_AutoLevel;
     CFStringRef kVTProfileLevel_H264_Main_4_2;
     CFStringRef kVTProfileLevel_H264_Main_5_1;
     CFStringRef kVTProfileLevel_H264_Main_5_2;
     CFStringRef kVTProfileLevel_H264_Main_AutoLevel;
     CFStringRef kVTProfileLevel_H264_High_3_0;
     CFStringRef kVTProfileLevel_H264_High_3_1;
     CFStringRef kVTProfileLevel_H264_High_3_2;
     CFStringRef kVTProfileLevel_H264_High_4_0;
     CFStringRef kVTProfileLevel_H264_High_4_1;
     CFStringRef kVTProfileLevel_H264_High_4_2;
     CFStringRef kVTProfileLevel_H264_High_5_1;
     CFStringRef kVTProfileLevel_H264_High_5_2;
     CFStringRef kVTProfileLevel_H264_High_AutoLevel;
 
     CFStringRef kVTCompressionPropertyKey_RealTime;
 
     CFStringRef kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder;
     CFStringRef kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder;
 } compat_keys;
 
 #define GET_SYM(symbol, defaultVal)                                     \
 do{                                                                     \
70ebc05b
     CFStringRef cfstr = *(CFStringRef*)dlsym(RTLD_DEFAULT, #symbol);    \
dcd3418a
     if(!cfstr)                                                          \
         compat_keys.symbol = CFSTR(defaultVal);                         \
     else                                                                \
70ebc05b
         compat_keys.symbol = cfstr;                                     \
dcd3418a
 }while(0)
 
 static pthread_once_t once_ctrl = PTHREAD_ONCE_INIT;
 
 static void loadVTEncSymbols(){
     GET_SYM(kCVImageBufferColorPrimaries_ITU_R_2020,   "ITU_R_2020");
     GET_SYM(kCVImageBufferTransferFunction_ITU_R_2020, "ITU_R_2020");
     GET_SYM(kCVImageBufferYCbCrMatrix_ITU_R_2020,      "ITU_R_2020");
 
     GET_SYM(kVTCompressionPropertyKey_H264EntropyMode, "H264EntropyMode");
     GET_SYM(kVTH264EntropyMode_CAVLC, "CAVLC");
     GET_SYM(kVTH264EntropyMode_CABAC, "CABAC");
 
     GET_SYM(kVTProfileLevel_H264_Baseline_4_0,       "H264_Baseline_4_0");
     GET_SYM(kVTProfileLevel_H264_Baseline_4_2,       "H264_Baseline_4_2");
     GET_SYM(kVTProfileLevel_H264_Baseline_5_0,       "H264_Baseline_5_0");
     GET_SYM(kVTProfileLevel_H264_Baseline_5_1,       "H264_Baseline_5_1");
     GET_SYM(kVTProfileLevel_H264_Baseline_5_2,       "H264_Baseline_5_2");
     GET_SYM(kVTProfileLevel_H264_Baseline_AutoLevel, "H264_Baseline_AutoLevel");
     GET_SYM(kVTProfileLevel_H264_Main_4_2,           "H264_Main_4_2");
     GET_SYM(kVTProfileLevel_H264_Main_5_1,           "H264_Main_5_1");
     GET_SYM(kVTProfileLevel_H264_Main_5_2,           "H264_Main_5_2");
     GET_SYM(kVTProfileLevel_H264_Main_AutoLevel,     "H264_Main_AutoLevel");
     GET_SYM(kVTProfileLevel_H264_High_3_0,           "H264_High_3_0");
     GET_SYM(kVTProfileLevel_H264_High_3_1,           "H264_High_3_1");
     GET_SYM(kVTProfileLevel_H264_High_3_2,           "H264_High_3_2");
     GET_SYM(kVTProfileLevel_H264_High_4_0,           "H264_High_4_0");
     GET_SYM(kVTProfileLevel_H264_High_4_1,           "H264_High_4_1");
     GET_SYM(kVTProfileLevel_H264_High_4_2,           "H264_High_4_2");
     GET_SYM(kVTProfileLevel_H264_High_5_1,           "H264_High_5_1");
     GET_SYM(kVTProfileLevel_H264_High_5_2,           "H264_High_5_2");
     GET_SYM(kVTProfileLevel_H264_High_AutoLevel,     "H264_High_AutoLevel");
 
     GET_SYM(kVTCompressionPropertyKey_RealTime, "RealTime");
 
     GET_SYM(kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,
             "EnableHardwareAcceleratedVideoEncoder");
     GET_SYM(kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder,
             "RequireHardwareAcceleratedVideoEncoder");
 }
dc750194
 
3af71ac3
 typedef enum VT_H264Profile {
     H264_PROF_AUTO,
     H264_PROF_BASELINE,
     H264_PROF_MAIN,
     H264_PROF_HIGH,
     H264_PROF_COUNT
 } VT_H264Profile;
 
645df431
 typedef enum VTH264Entropy{
     VT_ENTROPY_NOT_SET,
     VT_CAVLC,
     VT_CABAC
 } VTH264Entropy;
 
3af71ac3
 static const uint8_t start_code[] = { 0, 0, 0, 1 };
 
9ea91e41
 typedef struct ExtraSEI {
   void *data;
   size_t size;
 } ExtraSEI;
 
3af71ac3
 typedef struct BufNode {
     CMSampleBufferRef cm_buffer;
9ea91e41
     ExtraSEI *sei;
3af71ac3
     struct BufNode* next;
     int error;
 } BufNode;
 
 typedef struct VTEncContext {
     AVClass *class;
     VTCompressionSessionRef session;
dc750194
     CFStringRef ycbcr_matrix;
     CFStringRef color_primaries;
     CFStringRef transfer_function;
3af71ac3
 
     pthread_mutex_t lock;
     pthread_cond_t  cv_sample_sent;
 
     int async_error;
 
     BufNode *q_head;
     BufNode *q_tail;
 
     int64_t frame_ct_out;
     int64_t frame_ct_in;
 
     int64_t first_pts;
     int64_t dts_delta;
 
     int64_t profile;
     int64_t level;
645df431
     int64_t entropy;
ca429e72
     int64_t realtime;
4b806081
     int64_t frames_before;
     int64_t frames_after;
3af71ac3
 
fbe0cf8c
     int64_t allow_sw;
 
3af71ac3
     bool flushing;
     bool has_b_frames;
     bool warned_color_range;
9ea91e41
     bool a53_cc;
3af71ac3
 } VTEncContext;
 
98a3355c
 static int vtenc_populate_extradata(AVCodecContext   *avctx,
                                     CMVideoCodecType codec_type,
                                     CFStringRef      profile_level,
                                     CFNumberRef      gamma_level,
                                     CFDictionaryRef  enc_info,
                                     CFDictionaryRef  pixel_buffer_info);
 
d7cf3610
 /**
  * NULL-safe release of *refPtr, and sets value to NULL.
  */
 static void vt_release_num(CFNumberRef* refPtr){
     if (!*refPtr) {
         return;
     }
 
     CFRelease(*refPtr);
     *refPtr = NULL;
 }
 
3af71ac3
 static void set_async_error(VTEncContext *vtctx, int err)
 {
     BufNode *info;
 
     pthread_mutex_lock(&vtctx->lock);
 
     vtctx->async_error = err;
 
     info = vtctx->q_head;
     vtctx->q_head = vtctx->q_tail = NULL;
 
     while (info) {
         BufNode *next = info->next;
         CFRelease(info->cm_buffer);
         av_free(info);
         info = next;
     }
 
     pthread_mutex_unlock(&vtctx->lock);
 }
 
aa413b81
 static void clear_frame_queue(VTEncContext *vtctx)
 {
     set_async_error(vtctx, 0);
 }
 
9ea91e41
 static int vtenc_q_pop(VTEncContext *vtctx, bool wait, CMSampleBufferRef *buf, ExtraSEI **sei)
3af71ac3
 {
     BufNode *info;
 
     pthread_mutex_lock(&vtctx->lock);
 
     if (vtctx->async_error) {
         pthread_mutex_unlock(&vtctx->lock);
         return vtctx->async_error;
     }
 
     if (vtctx->flushing && vtctx->frame_ct_in == vtctx->frame_ct_out) {
         *buf = NULL;
 
         pthread_mutex_unlock(&vtctx->lock);
         return 0;
     }
 
     while (!vtctx->q_head && !vtctx->async_error && wait) {
         pthread_cond_wait(&vtctx->cv_sample_sent, &vtctx->lock);
     }
 
     if (!vtctx->q_head) {
         pthread_mutex_unlock(&vtctx->lock);
         *buf = NULL;
         return 0;
     }
 
     info = vtctx->q_head;
     vtctx->q_head = vtctx->q_head->next;
     if (!vtctx->q_head) {
         vtctx->q_tail = NULL;
     }
 
     pthread_mutex_unlock(&vtctx->lock);
 
     *buf = info->cm_buffer;
9ea91e41
     if (sei && *buf) {
         *sei = info->sei;
     } else if (info->sei) {
         if (info->sei->data) av_free(info->sei->data);
         av_free(info->sei);
     }
3af71ac3
     av_free(info);
 
     vtctx->frame_ct_out++;
 
     return 0;
 }
 
9ea91e41
 static void vtenc_q_push(VTEncContext *vtctx, CMSampleBufferRef buffer, ExtraSEI *sei)
3af71ac3
 {
     BufNode *info = av_malloc(sizeof(BufNode));
     if (!info) {
         set_async_error(vtctx, AVERROR(ENOMEM));
         return;
     }
 
     CFRetain(buffer);
     info->cm_buffer = buffer;
9ea91e41
     info->sei = sei;
3af71ac3
     info->next = NULL;
 
     pthread_mutex_lock(&vtctx->lock);
     pthread_cond_signal(&vtctx->cv_sample_sent);
 
     if (!vtctx->q_head) {
         vtctx->q_head = info;
     } else {
         vtctx->q_tail->next = info;
     }
 
     vtctx->q_tail = info;
 
     pthread_mutex_unlock(&vtctx->lock);
 }
 
c5a5ca75
 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;
 }
 
3af71ac3
 static CMVideoCodecType get_cm_codec_type(enum AVCodecID id)
 {
     switch (id) {
     case AV_CODEC_ID_H264: return kCMVideoCodecType_H264;
     default:               return 0;
     }
 }
 
 /**
  * Get the parameter sets from a CMSampleBufferRef.
  * @param dst If *dst isn't NULL, the parameters are copied into existing
  *            memory. *dst_size must be set accordingly when *dst != NULL.
  *            If *dst is NULL, it will be allocated.
  *            In all cases, *dst_size is set to the number of bytes used starting
  *            at *dst.
  */
 static int get_params_size(
     AVCodecContext              *avctx,
     CMVideoFormatDescriptionRef vid_fmt,
     size_t                      *size)
 {
     size_t total_size = 0;
     size_t ps_count;
c9ad357a
     int is_count_bad = 0;
3af71ac3
     size_t i;
     int status;
     status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt,
                                                                 0,
                                                                 NULL,
                                                                 NULL,
                                                                 &ps_count,
                                                                 NULL);
     if (status) {
c9ad357a
         is_count_bad = 1;
         ps_count     = 0;
         status       = 0;
3af71ac3
     }
 
c9ad357a
     for (i = 0; i < ps_count || is_count_bad; i++) {
3af71ac3
         const uint8_t *ps;
         size_t ps_size;
         status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt,
                                                                     i,
                                                                     &ps,
                                                                     &ps_size,
                                                                     NULL,
                                                                     NULL);
c9ad357a
         if (status) {
             /*
              * When ps_count is invalid, status != 0 ends the loop normally
              * unless we didn't get any parameter sets.
              */
             if (i > 0 && is_count_bad) status = 0;
 
             break;
3af71ac3
         }
 
         total_size += ps_size + sizeof(start_code);
     }
 
c9ad357a
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error getting parameter set sizes: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
3af71ac3
     *size = total_size;
     return 0;
 }
 
 static int copy_param_sets(
     AVCodecContext              *avctx,
     CMVideoFormatDescriptionRef vid_fmt,
     uint8_t                     *dst,
     size_t                      dst_size)
 {
     size_t ps_count;
c9ad357a
     int is_count_bad = 0;
3af71ac3
     int status;
     size_t offset = 0;
     size_t i;
 
     status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt,
                                                                 0,
                                                                 NULL,
                                                                 NULL,
                                                                 &ps_count,
                                                                 NULL);
     if (status) {
c9ad357a
         is_count_bad = 1;
         ps_count     = 0;
         status       = 0;
3af71ac3
     }
 
c9ad357a
 
     for (i = 0; i < ps_count || is_count_bad; i++) {
3af71ac3
         const uint8_t *ps;
         size_t ps_size;
         size_t next_offset;
 
         status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt,
                                                                     i,
                                                                     &ps,
                                                                     &ps_size,
                                                                     NULL,
                                                                     NULL);
         if (status) {
c9ad357a
             if (i > 0 && is_count_bad) status = 0;
 
             break;
3af71ac3
         }
 
         next_offset = offset + sizeof(start_code) + ps_size;
         if (dst_size < next_offset) {
             av_log(avctx, AV_LOG_ERROR, "Error: buffer too small for parameter sets.\n");
             return AVERROR_BUFFER_TOO_SMALL;
         }
 
         memcpy(dst + offset, start_code, sizeof(start_code));
         offset += sizeof(start_code);
 
         memcpy(dst + offset, ps, ps_size);
         offset = next_offset;
     }
 
c9ad357a
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error getting parameter set data: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
3af71ac3
     return 0;
 }
 
 static int set_extradata(AVCodecContext *avctx, CMSampleBufferRef sample_buffer)
 {
     CMVideoFormatDescriptionRef vid_fmt;
     size_t total_size;
     int status;
 
     vid_fmt = CMSampleBufferGetFormatDescription(sample_buffer);
     if (!vid_fmt) {
         av_log(avctx, AV_LOG_ERROR, "No video format.\n");
         return AVERROR_EXTERNAL;
     }
 
     status = get_params_size(avctx, vid_fmt, &total_size);
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Could not get parameter sets.\n");
         return status;
     }
 
98a3355c
     avctx->extradata = av_mallocz(total_size + AV_INPUT_BUFFER_PADDING_SIZE);
3af71ac3
     if (!avctx->extradata) {
         return AVERROR(ENOMEM);
     }
     avctx->extradata_size = total_size;
 
     status = copy_param_sets(avctx, vid_fmt, avctx->extradata, total_size);
 
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Could not copy param sets.\n");
         return status;
     }
 
     return 0;
 }
 
 static void vtenc_output_callback(
ecba35bb
     void *ctx,
3af71ac3
     void *sourceFrameCtx,
     OSStatus status,
     VTEncodeInfoFlags flags,
ecba35bb
     CMSampleBufferRef sample_buffer)
3af71ac3
 {
     AVCodecContext *avctx = ctx;
     VTEncContext   *vtctx = avctx->priv_data;
9ea91e41
     ExtraSEI *sei = sourceFrameCtx;
3af71ac3
 
     if (vtctx->async_error) {
         if(sample_buffer) CFRelease(sample_buffer);
         return;
     }
 
     if (status || !sample_buffer) {
         av_log(avctx, AV_LOG_ERROR, "Error encoding frame: %d\n", (int)status);
         set_async_error(vtctx, AVERROR_EXTERNAL);
         return;
     }
 
     if (!avctx->extradata && (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) {
         int set_status = set_extradata(avctx, sample_buffer);
         if (set_status) {
             set_async_error(vtctx, set_status);
             return;
         }
     }
 
9ea91e41
     vtenc_q_push(vtctx, sample_buffer, sei);
3af71ac3
 }
 
 static int get_length_code_size(
     AVCodecContext    *avctx,
     CMSampleBufferRef sample_buffer,
     size_t            *size)
 {
     CMVideoFormatDescriptionRef vid_fmt;
     int isize;
     int status;
 
     vid_fmt = CMSampleBufferGetFormatDescription(sample_buffer);
     if (!vid_fmt) {
         av_log(avctx, AV_LOG_ERROR, "Error getting buffer format description.\n");
         return AVERROR_EXTERNAL;
     }
 
     status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt,
                                                                 0,
                                                                 NULL,
                                                                 NULL,
                                                                 NULL,
                                                                 &isize);
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error getting length code size: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
     *size = isize;
     return 0;
 }
 
 /*
  * Returns true on success.
  *
  * If profile_level_val is NULL and this method returns true, don't specify the
  * profile/level to the encoder.
  */
 static bool get_vt_profile_level(AVCodecContext *avctx,
                                  CFStringRef    *profile_level_val)
 {
     VTEncContext *vtctx = avctx->priv_data;
     int64_t profile = vtctx->profile;
 
     if (profile == H264_PROF_AUTO && vtctx->level) {
         //Need to pick a profile if level is not auto-selected.
         profile = vtctx->has_b_frames ? H264_PROF_MAIN : H264_PROF_BASELINE;
     }
 
     *profile_level_val = NULL;
 
     switch (profile) {
         case H264_PROF_AUTO:
             return true;
 
         case H264_PROF_BASELINE:
             switch (vtctx->level) {
dcd3418a
                 case  0: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Baseline_AutoLevel; break;
3af71ac3
                 case 13: *profile_level_val = kVTProfileLevel_H264_Baseline_1_3;       break;
                 case 30: *profile_level_val = kVTProfileLevel_H264_Baseline_3_0;       break;
                 case 31: *profile_level_val = kVTProfileLevel_H264_Baseline_3_1;       break;
                 case 32: *profile_level_val = kVTProfileLevel_H264_Baseline_3_2;       break;
dcd3418a
                 case 40: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Baseline_4_0;       break;
3af71ac3
                 case 41: *profile_level_val = kVTProfileLevel_H264_Baseline_4_1;       break;
dcd3418a
                 case 42: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Baseline_4_2;       break;
                 case 50: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Baseline_5_0;       break;
                 case 51: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Baseline_5_1;       break;
                 case 52: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Baseline_5_2;       break;
3af71ac3
             }
             break;
 
         case H264_PROF_MAIN:
             switch (vtctx->level) {
dcd3418a
                 case  0: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Main_AutoLevel; break;
3af71ac3
                 case 30: *profile_level_val = kVTProfileLevel_H264_Main_3_0;       break;
                 case 31: *profile_level_val = kVTProfileLevel_H264_Main_3_1;       break;
                 case 32: *profile_level_val = kVTProfileLevel_H264_Main_3_2;       break;
                 case 40: *profile_level_val = kVTProfileLevel_H264_Main_4_0;       break;
                 case 41: *profile_level_val = kVTProfileLevel_H264_Main_4_1;       break;
dcd3418a
                 case 42: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Main_4_2;       break;
3af71ac3
                 case 50: *profile_level_val = kVTProfileLevel_H264_Main_5_0;       break;
dcd3418a
                 case 51: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Main_5_1;       break;
                 case 52: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_Main_5_2;       break;
3af71ac3
             }
             break;
 
         case H264_PROF_HIGH:
             switch (vtctx->level) {
dcd3418a
                 case  0: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_AutoLevel; break;
                 case 30: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_3_0;       break;
                 case 31: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_3_1;       break;
                 case 32: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_3_2;       break;
                 case 40: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_4_0;       break;
                 case 41: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_4_1;       break;
                 case 42: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_4_2;       break;
3af71ac3
                 case 50: *profile_level_val = kVTProfileLevel_H264_High_5_0;       break;
dcd3418a
                 case 51: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_5_1;       break;
                 case 52: *profile_level_val =
                                   compat_keys.kVTProfileLevel_H264_High_5_2;       break;
3af71ac3
             }
             break;
     }
 
     if (!*profile_level_val) {
         av_log(avctx, AV_LOG_ERROR, "Invalid Profile/Level.\n");
         return false;
     }
 
     return true;
 }
 
d7cf3610
 static int get_cv_pixel_format(AVCodecContext* avctx,
                                enum AVPixelFormat fmt,
                                enum AVColorRange range,
                                int* av_pixel_format,
                                int* range_guessed)
 {
     if (range_guessed) *range_guessed = range != AVCOL_RANGE_MPEG &&
                                         range != AVCOL_RANGE_JPEG;
 
     //MPEG range is used when no range is set
     if (fmt == AV_PIX_FMT_NV12) {
         *av_pixel_format = range == AVCOL_RANGE_JPEG ?
                                         kCVPixelFormatType_420YpCbCr8BiPlanarFullRange :
                                         kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
     } else if (fmt == AV_PIX_FMT_YUV420P) {
         *av_pixel_format = range == AVCOL_RANGE_JPEG ?
                                         kCVPixelFormatType_420YpCbCr8PlanarFullRange :
                                         kCVPixelFormatType_420YpCbCr8Planar;
     } else {
         return AVERROR(EINVAL);
     }
 
     return 0;
 }
 
dc750194
 static void add_color_attr(AVCodecContext *avctx, CFMutableDictionaryRef dict) {
     VTEncContext *vtctx = avctx->priv_data;
 
     if (vtctx->color_primaries) {
         CFDictionarySetValue(dict,
                              kCVImageBufferColorPrimariesKey,
                              vtctx->color_primaries);
     }
 
     if (vtctx->transfer_function) {
         CFDictionarySetValue(dict,
                              kCVImageBufferTransferFunctionKey,
                              vtctx->transfer_function);
     }
 
     if (vtctx->ycbcr_matrix) {
         CFDictionarySetValue(dict,
                              kCVImageBufferYCbCrMatrixKey,
                              vtctx->ycbcr_matrix);
     }
 }
 
d7cf3610
 static int create_cv_pixel_buffer_info(AVCodecContext* avctx,
                                        CFMutableDictionaryRef* dict)
 {
     CFNumberRef cv_color_format_num = NULL;
     CFNumberRef width_num = NULL;
     CFNumberRef height_num = NULL;
     CFMutableDictionaryRef pixel_buffer_info = NULL;
     int cv_color_format;
     int status = get_cv_pixel_format(avctx,
                                      avctx->pix_fmt,
                                      avctx->color_range,
                                      &cv_color_format,
                                      NULL);
     if (status) return status;
 
     pixel_buffer_info = CFDictionaryCreateMutable(
                             kCFAllocatorDefault,
                             20,
                             &kCFCopyStringDictionaryKeyCallBacks,
                             &kCFTypeDictionaryValueCallBacks);
 
     if (!pixel_buffer_info) goto pbinfo_nomem;
 
     cv_color_format_num = CFNumberCreate(kCFAllocatorDefault,
                                          kCFNumberSInt32Type,
                                          &cv_color_format);
     if (!cv_color_format_num) goto pbinfo_nomem;
 
     CFDictionarySetValue(pixel_buffer_info,
                          kCVPixelBufferPixelFormatTypeKey,
                          cv_color_format_num);
     vt_release_num(&cv_color_format_num);
 
     width_num = CFNumberCreate(kCFAllocatorDefault,
                                kCFNumberSInt32Type,
                                &avctx->width);
     if (!width_num) return AVERROR(ENOMEM);
 
     CFDictionarySetValue(pixel_buffer_info,
                          kCVPixelBufferWidthKey,
                          width_num);
     vt_release_num(&width_num);
 
     height_num = CFNumberCreate(kCFAllocatorDefault,
                                 kCFNumberSInt32Type,
                                 &avctx->height);
     if (!height_num) goto pbinfo_nomem;
 
     CFDictionarySetValue(pixel_buffer_info,
                          kCVPixelBufferHeightKey,
                          height_num);
     vt_release_num(&height_num);
 
dc750194
     add_color_attr(avctx, pixel_buffer_info);
 
d7cf3610
     *dict = pixel_buffer_info;
     return 0;
 
 pbinfo_nomem:
     vt_release_num(&cv_color_format_num);
     vt_release_num(&width_num);
     vt_release_num(&height_num);
     if (pixel_buffer_info) CFRelease(pixel_buffer_info);
 
     return AVERROR(ENOMEM);
 }
 
dc750194
 static int get_cv_color_primaries(AVCodecContext *avctx,
                                   CFStringRef *primaries)
 {
     enum AVColorPrimaries pri = avctx->color_primaries;
     switch (pri) {
         case AVCOL_PRI_UNSPECIFIED:
             *primaries = NULL;
             break;
 
         case AVCOL_PRI_BT709:
             *primaries = kCVImageBufferColorPrimaries_ITU_R_709_2;
             break;
 
         case AVCOL_PRI_BT2020:
dcd3418a
             *primaries = compat_keys.kCVImageBufferColorPrimaries_ITU_R_2020;
dc750194
             break;
 
         default:
             av_log(avctx, AV_LOG_ERROR, "Color primaries %s is not supported.\n", av_color_primaries_name(pri));
             *primaries = NULL;
             return -1;
     }
 
     return 0;
 }
 
 static int get_cv_transfer_function(AVCodecContext *avctx,
                                     CFStringRef *transfer_fnc,
                                     CFNumberRef *gamma_level)
 {
     enum AVColorTransferCharacteristic trc = avctx->color_trc;
     Float32 gamma;
     *gamma_level = NULL;
 
     switch (trc) {
         case AVCOL_TRC_UNSPECIFIED:
             *transfer_fnc = NULL;
             break;
 
         case AVCOL_TRC_BT709:
             *transfer_fnc = kCVImageBufferTransferFunction_ITU_R_709_2;
             break;
 
         case AVCOL_TRC_SMPTE240M:
             *transfer_fnc = kCVImageBufferTransferFunction_SMPTE_240M_1995;
             break;
 
         case AVCOL_TRC_GAMMA22:
             gamma = 2.2;
             *transfer_fnc = kCVImageBufferTransferFunction_UseGamma;
             *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma);
             break;
 
         case AVCOL_TRC_GAMMA28:
             gamma = 2.8;
             *transfer_fnc = kCVImageBufferTransferFunction_UseGamma;
             *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma);
             break;
 
         case AVCOL_TRC_BT2020_10:
         case AVCOL_TRC_BT2020_12:
dcd3418a
             *transfer_fnc = compat_keys.kCVImageBufferTransferFunction_ITU_R_2020;
dc750194
             break;
 
         default:
             av_log(avctx, AV_LOG_ERROR, "Transfer function %s is not supported.\n", av_color_transfer_name(trc));
             return -1;
     }
 
     return 0;
 }
 
 static int get_cv_ycbcr_matrix(AVCodecContext *avctx, CFStringRef *matrix) {
     switch(avctx->colorspace) {
         case AVCOL_SPC_BT709:
             *matrix = kCVImageBufferYCbCrMatrix_ITU_R_709_2;
             break;
 
         case AVCOL_SPC_UNSPECIFIED:
             *matrix = NULL;
             break;
 
         case AVCOL_SPC_BT470BG:
         case AVCOL_SPC_SMPTE170M:
             *matrix = kCVImageBufferYCbCrMatrix_ITU_R_601_4;
             break;
 
         case AVCOL_SPC_SMPTE240M:
             *matrix = kCVImageBufferYCbCrMatrix_SMPTE_240M_1995;
             break;
 
         case AVCOL_SPC_BT2020_NCL:
dcd3418a
             *matrix = compat_keys.kCVImageBufferYCbCrMatrix_ITU_R_2020;
dc750194
             break;
 
         default:
             av_log(avctx, AV_LOG_ERROR, "Color space %s is not supported.\n", av_color_space_name(avctx->colorspace));
             return -1;
     }
 
     return 0;
 }
 
98a3355c
 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)
3af71ac3
 {
98a3355c
     VTEncContext *vtctx = avctx->priv_data;
     SInt32       bit_rate = avctx->bit_rate;
0052f3f5
     SInt32       max_rate = avctx->rc_max_rate;
98a3355c
     CFNumberRef  bit_rate_num;
0052f3f5
     CFNumberRef  bytes_per_second;
     CFNumberRef  one_second;
     CFArrayRef   data_rate_limits;
     int64_t      bytes_per_second_value = 0;
     int64_t      one_second_value = 0;
     void         *nums[2];
98a3355c
 
     int status = VTCompressionSessionCreate(kCFAllocatorDefault,
                                             avctx->width,
                                             avctx->height,
                                             codec_type,
                                             enc_info,
                                             pixel_buffer_info,
                                             kCFAllocatorDefault,
                                             vtenc_output_callback,
                                             avctx,
                                             session);
3af71ac3
 
     if (status || !vtctx->session) {
         av_log(avctx, AV_LOG_ERROR, "Error: cannot create compression session: %d\n", status);
fbe0cf8c
 
 #if !TARGET_OS_IPHONE
         if (!vtctx->allow_sw) {
             av_log(avctx, AV_LOG_ERROR, "Try -allow_sw 1. The hardware encoder may be busy, or not supported.\n");
         }
 #endif
 
3af71ac3
         return AVERROR_EXTERNAL;
     }
 
     bit_rate_num = CFNumberCreate(kCFAllocatorDefault,
                                   kCFNumberSInt32Type,
                                   &bit_rate);
     if (!bit_rate_num) return AVERROR(ENOMEM);
 
     status = VTSessionSetProperty(vtctx->session,
                                   kVTCompressionPropertyKey_AverageBitRate,
                                   bit_rate_num);
     CFRelease(bit_rate_num);
 
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error setting bitrate property: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
0052f3f5
     bytes_per_second_value = max_rate >> 3;
     bytes_per_second = CFNumberCreate(kCFAllocatorDefault,
                                       kCFNumberSInt64Type,
                                       &bytes_per_second_value);
     if (!bytes_per_second) {
         return AVERROR(ENOMEM);
     }
     one_second_value = 1;
     one_second = CFNumberCreate(kCFAllocatorDefault,
                                 kCFNumberSInt64Type,
                                 &one_second_value);
     if (!one_second) {
         CFRelease(bytes_per_second);
         return AVERROR(ENOMEM);
     }
     nums[0] = bytes_per_second;
     nums[1] = one_second;
     data_rate_limits = CFArrayCreate(kCFAllocatorDefault,
                                      nums,
                                      2,
                                      &kCFTypeArrayCallBacks);
 
     if (!data_rate_limits) {
         CFRelease(bytes_per_second);
         CFRelease(one_second);
         return AVERROR(ENOMEM);
     }
     status = VTSessionSetProperty(vtctx->session,
                                   kVTCompressionPropertyKey_DataRateLimits,
                                   data_rate_limits);
 
     CFRelease(bytes_per_second);
     CFRelease(one_second);
     CFRelease(data_rate_limits);
 
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error setting max bitrate property: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
3af71ac3
     if (profile_level) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_ProfileLevel,
                                       profile_level);
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting profile/level property: %d\n", status);
         }
     }
 
     if (avctx->gop_size > 0) {
         CFNumberRef interval = CFNumberCreate(kCFAllocatorDefault,
                                               kCFNumberIntType,
                                               &avctx->gop_size);
272dd437
         if (!interval) {
             return AVERROR(ENOMEM);
         }
 
3af71ac3
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_MaxKeyFrameInterval,
                                       interval);
272dd437
         CFRelease(interval);
3af71ac3
 
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting 'max key-frame interval' property: %d\n", status);
             return AVERROR_EXTERNAL;
         }
     }
 
4b806081
     if (vtctx->frames_before) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_MoreFramesBeforeStart,
                                       kCFBooleanTrue);
 
         if (status == kVTPropertyNotSupportedErr) {
             av_log(avctx, AV_LOG_WARNING, "frames_before property is not supported on this device. Ignoring.\n");
         } else if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting frames_before property: %d\n", status);
         }
     }
 
     if (vtctx->frames_after) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_MoreFramesAfterEnd,
                                       kCFBooleanTrue);
 
         if (status == kVTPropertyNotSupportedErr) {
             av_log(avctx, AV_LOG_WARNING, "frames_after property is not supported on this device. Ignoring.\n");
         } else if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting frames_after property: %d\n", status);
         }
     }
 
c356b686
     if (avctx->sample_aspect_ratio.num != 0) {
         CFNumberRef num;
         CFNumberRef den;
         CFMutableDictionaryRef par;
         AVRational *avpar = &avctx->sample_aspect_ratio;
 
         av_reduce(&avpar->num, &avpar->den,
                    avpar->num,  avpar->den,
                   0xFFFFFFFF);
 
         num = CFNumberCreate(kCFAllocatorDefault,
                              kCFNumberIntType,
                              &avpar->num);
 
         den = CFNumberCreate(kCFAllocatorDefault,
                              kCFNumberIntType,
                              &avpar->den);
 
 
 
         par = CFDictionaryCreateMutable(kCFAllocatorDefault,
                                         2,
                                         &kCFCopyStringDictionaryKeyCallBacks,
                                         &kCFTypeDictionaryValueCallBacks);
 
         if (!par || !num || !den) {
             if (par) CFRelease(par);
             if (num) CFRelease(num);
             if (den) CFRelease(den);
 
             return AVERROR(ENOMEM);
         }
 
         CFDictionarySetValue(
             par,
             kCMFormatDescriptionKey_PixelAspectRatioHorizontalSpacing,
             num);
 
         CFDictionarySetValue(
             par,
             kCMFormatDescriptionKey_PixelAspectRatioVerticalSpacing,
             den);
 
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_PixelAspectRatio,
                                       par);
 
         CFRelease(par);
         CFRelease(num);
         CFRelease(den);
 
         if (status) {
             av_log(avctx,
                    AV_LOG_ERROR,
                    "Error setting pixel aspect ratio to %d:%d: %d.\n",
                    avctx->sample_aspect_ratio.num,
                    avctx->sample_aspect_ratio.den,
                    status);
 
             return AVERROR_EXTERNAL;
         }
     }
 
98a3355c
 
     if (vtctx->transfer_function) {
dc750194
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_TransferFunction,
                                       vtctx->transfer_function);
 
         if (status) {
             av_log(avctx, AV_LOG_WARNING, "Could not set transfer function: %d\n", status);
         }
     }
 
98a3355c
 
     if (vtctx->ycbcr_matrix) {
dc750194
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_YCbCrMatrix,
                                       vtctx->ycbcr_matrix);
 
         if (status) {
             av_log(avctx, AV_LOG_WARNING, "Could not set ycbcr matrix: %d\n", status);
         }
     }
 
98a3355c
 
     if (vtctx->color_primaries) {
dc750194
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_ColorPrimaries,
                                       vtctx->color_primaries);
 
         if (status) {
             av_log(avctx, AV_LOG_WARNING, "Could not set color primaries: %d\n", status);
         }
     }
 
98a3355c
     if (gamma_level) {
dc750194
         status = VTSessionSetProperty(vtctx->session,
                                       kCVImageBufferGammaLevelKey,
                                       gamma_level);
 
         if (status) {
             av_log(avctx, AV_LOG_WARNING, "Could not set gamma level: %d\n", status);
         }
     }
 
3af71ac3
     if (!vtctx->has_b_frames) {
         status = VTSessionSetProperty(vtctx->session,
                                       kVTCompressionPropertyKey_AllowFrameReordering,
                                       kCFBooleanFalse);
 
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting 'allow frame reordering' property: %d\n", status);
             return AVERROR_EXTERNAL;
         }
     }
 
645df431
     if (vtctx->entropy != VT_ENTROPY_NOT_SET) {
         CFStringRef entropy = vtctx->entropy == VT_CABAC ?
dcd3418a
                                 compat_keys.kVTH264EntropyMode_CABAC:
                                 compat_keys.kVTH264EntropyMode_CAVLC;
645df431
 
         status = VTSessionSetProperty(vtctx->session,
dcd3418a
                                       compat_keys.kVTCompressionPropertyKey_H264EntropyMode,
645df431
                                       entropy);
 
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting entropy property: %d\n", status);
         }
     }
 
ca429e72
     if (vtctx->realtime) {
         status = VTSessionSetProperty(vtctx->session,
dcd3418a
                                       compat_keys.kVTCompressionPropertyKey_RealTime,
ca429e72
                                       kCFBooleanTrue);
 
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error setting realtime property: %d\n", status);
         }
     }
 
3af71ac3
     status = VTCompressionSessionPrepareToEncodeFrames(vtctx->session);
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error: cannot prepare encoder: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
98a3355c
     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;
 
dcd3418a
     pthread_once(&once_ctrl, loadVTEncSymbols);
 
98a3355c
     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) {
dcd3418a
         CFDictionarySetValue(enc_info,
                              compat_keys.kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder,
                              kCFBooleanTrue);
98a3355c
     } else {
dcd3418a
         CFDictionarySetValue(enc_info,
                              compat_keys.kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,
                              kCFBooleanTrue);
98a3355c
     }
 #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;
     }
 
3af71ac3
     pthread_mutex_init(&vtctx->lock, NULL);
     pthread_cond_init(&vtctx->cv_sample_sent, NULL);
     vtctx->dts_delta = vtctx->has_b_frames ? -1 : 0;
 
98a3355c
     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;
 
abe05892
     status = VTSessionCopyProperty(vtctx->session,
                                    kVTCompressionPropertyKey_AllowFrameReordering,
                                    kCFAllocatorDefault,
                                    &has_b_frames_cfbool);
 
     if (!status) {
         //Some devices don't output B-frames for main profile, even if requested.
         vtctx->has_b_frames = CFBooleanGetValue(has_b_frames_cfbool);
         CFRelease(has_b_frames_cfbool);
     }
     avctx->has_b_frames = vtctx->has_b_frames;
 
98a3355c
 init_cleanup:
     if (gamma_level)
         CFRelease(gamma_level);
 
     if (pixel_buffer_info)
         CFRelease(pixel_buffer_info);
 
     CFRelease(enc_info);
 
     return status;
3af71ac3
 }
 
 static void vtenc_get_frame_info(CMSampleBufferRef buffer, bool *is_key_frame)
 {
     CFArrayRef      attachments;
     CFDictionaryRef attachment;
     CFBooleanRef    not_sync;
     CFIndex         len;
 
     attachments = CMSampleBufferGetSampleAttachmentsArray(buffer, false);
     len = !attachments ? 0 : CFArrayGetCount(attachments);
 
     if (!len) {
         *is_key_frame = true;
         return;
     }
 
     attachment = CFArrayGetValueAtIndex(attachments, 0);
 
     if (CFDictionaryGetValueIfPresent(attachment,
                                       kCMSampleAttachmentKey_NotSync,
                                       (const void **)&not_sync))
     {
         *is_key_frame = !CFBooleanGetValue(not_sync);
     } else {
         *is_key_frame = true;
     }
 }
 
9875695e
 static int is_post_sei_nal_type(int nal_type){
     return nal_type != H264_NAL_SEI &&
            nal_type != H264_NAL_SPS &&
            nal_type != H264_NAL_PPS &&
            nal_type != H264_NAL_AUD;
 }
 
 /*
  * Finds the sei message start/size of type find_sei_type.
  * If more than one of that type exists, the last one is returned.
  */
 static int find_sei_end(AVCodecContext *avctx,
                         uint8_t        *nal_data,
                         size_t          nal_size,
                         uint8_t       **sei_end)
 {
     int nal_type;
     size_t sei_payload_size = 0;
     int sei_payload_type = 0;
     *sei_end = NULL;
     uint8_t *nal_start = nal_data;
 
     if (!nal_size)
         return 0;
 
     nal_type = *nal_data & 0x1F;
     if (nal_type != H264_NAL_SEI)
         return 0;
 
     nal_data++;
     nal_size--;
 
     if (nal_data[nal_size - 1] == 0x80)
         nal_size--;
 
     while (nal_size > 0 && *nal_data > 0) {
         do{
             sei_payload_type += *nal_data;
             nal_data++;
             nal_size--;
         } while (nal_size > 0 && *nal_data == 0xFF);
 
         if (!nal_size) {
             av_log(avctx, AV_LOG_ERROR, "Unexpected end of SEI NAL Unit parsing type.\n");
             return AVERROR_INVALIDDATA;
         }
 
         do{
             sei_payload_size += *nal_data;
             nal_data++;
             nal_size--;
         } while (nal_size > 0 && *nal_data == 0xFF);
 
         if (nal_size < sei_payload_size) {
             av_log(avctx, AV_LOG_ERROR, "Unexpected end of SEI NAL Unit parsing size.\n");
             return AVERROR_INVALIDDATA;
         }
 
         nal_data += sei_payload_size;
         nal_size -= sei_payload_size;
     }
 
     *sei_end = nal_data;
 
     return nal_data - nal_start + 1;
 }
 
 /**
  * Copies the data inserting emulation prevention bytes as needed.
  * Existing data in the destination can be taken into account by providing
  * dst with a dst_offset > 0.
  *
  * @return The number of bytes copied on success. On failure, the negative of
  *         the number of bytes needed to copy src is returned.
  */
 static int copy_emulation_prev(const uint8_t *src,
                                size_t         src_size,
                                uint8_t       *dst,
                                ssize_t        dst_offset,
                                size_t         dst_size)
 {
     int zeros = 0;
     int wrote_bytes;
     uint8_t* dst_start;
     uint8_t* dst_end = dst + dst_size;
     const uint8_t* src_end = src + src_size;
     int start_at = dst_offset > 2 ? dst_offset - 2 : 0;
     int i;
     for (i = start_at; i < dst_offset && i < dst_size; i++) {
         if (!dst[i])
             zeros++;
         else
             zeros = 0;
     }
 
     dst += dst_offset;
     dst_start = dst;
     for (; src < src_end; src++, dst++) {
         if (zeros == 2) {
             int insert_ep3_byte = *src <= 3;
             if (insert_ep3_byte) {
                 if (dst < dst_end)
                     *dst = 3;
                 dst++;
             }
 
             zeros = 0;
         }
 
         if (dst < dst_end)
             *dst = *src;
 
         if (!*src)
             zeros++;
         else
             zeros = 0;
     }
 
     wrote_bytes = dst - dst_start;
 
     if (dst > dst_end)
         return -wrote_bytes;
 
     return wrote_bytes;
 }
 
 static int write_sei(const ExtraSEI *sei,
                      int             sei_type,
                      uint8_t        *dst,
                      size_t          dst_size)
 {
     uint8_t *sei_start = dst;
     size_t remaining_sei_size = sei->size;
     size_t remaining_dst_size = dst_size;
     int header_bytes;
     int bytes_written;
     ssize_t offset;
 
     if (!remaining_dst_size)
         return AVERROR_BUFFER_TOO_SMALL;
 
     while (sei_type && remaining_dst_size != 0) {
         int sei_byte = sei_type > 255 ? 255 : sei_type;
         *dst = sei_byte;
 
         sei_type -= sei_byte;
         dst++;
         remaining_dst_size--;
     }
 
     if (!dst_size)
         return AVERROR_BUFFER_TOO_SMALL;
 
     while (remaining_sei_size && remaining_dst_size != 0) {
         int size_byte = remaining_sei_size > 255 ? 255 : remaining_sei_size;
         *dst = size_byte;
 
         remaining_sei_size -= size_byte;
         dst++;
         remaining_dst_size--;
     }
 
     if (remaining_dst_size < sei->size)
         return AVERROR_BUFFER_TOO_SMALL;
 
     header_bytes = dst - sei_start;
 
     offset = header_bytes;
     bytes_written = copy_emulation_prev(sei->data,
                                         sei->size,
                                         sei_start,
                                         offset,
                                         dst_size);
     if (bytes_written < 0)
         return AVERROR_BUFFER_TOO_SMALL;
 
     bytes_written += header_bytes;
     return bytes_written;
 }
 
3af71ac3
 /**
  * Copies NAL units and replaces length codes with
  * H.264 Annex B start codes. On failure, the contents of
  * dst_data may have been modified.
  *
  * @param length_code_size Byte length of each length code
9875695e
  * @param sample_buffer NAL units prefixed with length codes.
  * @param sei Optional A53 closed captions SEI data.
3af71ac3
  * @param dst_data Must be zeroed before calling this function.
  *                 Contains the copied NAL units prefixed with
  *                 start codes when the function returns
  *                 successfully.
  * @param dst_size Length of dst_data
  * @return 0 on success
  *         AVERROR_INVALIDDATA if length_code_size is invalid
  *         AVERROR_BUFFER_TOO_SMALL if dst_data is too small
  *         or if a length_code in src_data specifies data beyond
  *         the end of its buffer.
  */
 static int copy_replace_length_codes(
c5a5ca75
     AVCodecContext *avctx,
3af71ac3
     size_t        length_code_size,
c5a5ca75
     CMSampleBufferRef sample_buffer,
9875695e
     ExtraSEI      *sei,
3af71ac3
     uint8_t       *dst_data,
     size_t        dst_size)
 {
c5a5ca75
     size_t src_size = CMSampleBufferGetTotalSampleSize(sample_buffer);
3af71ac3
     size_t remaining_src_size = src_size;
     size_t remaining_dst_size = dst_size;
c5a5ca75
     size_t src_offset = 0;
9875695e
     int wrote_sei = 0;
c5a5ca75
     int status;
     uint8_t size_buf[4];
9875695e
     uint8_t nal_type;
c5a5ca75
     CMBlockBufferRef block = CMSampleBufferGetDataBuffer(sample_buffer);
3af71ac3
 
     if (length_code_size > 4) {
         return AVERROR_INVALIDDATA;
     }
 
     while (remaining_src_size > 0) {
         size_t curr_src_len;
         size_t curr_dst_len;
         size_t box_len = 0;
         size_t i;
 
         uint8_t       *dst_box;
c5a5ca75
 
         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;
         }
3af71ac3
 
9875695e
         status = CMBlockBufferCopyDataBytes(block,
                                             src_offset + length_code_size,
                                             1,
                                             &nal_type);
 
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Cannot copy type: %d\n", status);
             return AVERROR_EXTERNAL;
         }
 
         nal_type &= 0x1F;
 
3af71ac3
         for (i = 0; i < length_code_size; i++) {
             box_len <<= 8;
c5a5ca75
             box_len |= size_buf[i];
3af71ac3
         }
 
9875695e
         if (sei && !wrote_sei && is_post_sei_nal_type(nal_type)) {
             //No SEI NAL unit - insert.
             int wrote_bytes;
 
             memcpy(dst_data, start_code, sizeof(start_code));
             dst_data += sizeof(start_code);
             remaining_dst_size -= sizeof(start_code);
 
             *dst_data = H264_NAL_SEI;
             dst_data++;
             remaining_dst_size--;
 
             wrote_bytes = write_sei(sei,
                                     SEI_TYPE_USER_DATA_REGISTERED,
                                     dst_data,
                                     remaining_dst_size);
 
             if (wrote_bytes < 0)
                 return wrote_bytes;
 
             remaining_dst_size -= wrote_bytes;
             dst_data += wrote_bytes;
 
             if (remaining_dst_size <= 0)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             *dst_data = 0x80;
 
             dst_data++;
             remaining_dst_size--;
 
             wrote_sei = 1;
         }
 
3af71ac3
         curr_src_len = box_len + length_code_size;
         curr_dst_len = box_len + sizeof(start_code);
 
         if (remaining_src_size < curr_src_len) {
             return AVERROR_BUFFER_TOO_SMALL;
         }
 
         if (remaining_dst_size < curr_dst_len) {
             return AVERROR_BUFFER_TOO_SMALL;
         }
 
         dst_box = dst_data + sizeof(start_code);
 
         memcpy(dst_data, start_code, sizeof(start_code));
c5a5ca75
         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;
         }
3af71ac3
 
9875695e
         if (sei && !wrote_sei && nal_type == H264_NAL_SEI) {
             //Found SEI NAL unit - append.
             int wrote_bytes;
             int old_sei_length;
             int extra_bytes;
             uint8_t *new_sei;
             old_sei_length = find_sei_end(avctx, dst_box, box_len, &new_sei);
             if (old_sei_length < 0)
                 return status;
 
             wrote_bytes = write_sei(sei,
                                     SEI_TYPE_USER_DATA_REGISTERED,
                                     new_sei,
                                     remaining_dst_size - old_sei_length);
             if (wrote_bytes < 0)
                 return wrote_bytes;
 
             if (new_sei + wrote_bytes >= dst_data + remaining_dst_size)
                 return AVERROR_BUFFER_TOO_SMALL;
 
             new_sei[wrote_bytes++] = 0x80;
             extra_bytes = wrote_bytes - (dst_box + box_len - new_sei);
 
             dst_data += extra_bytes;
             remaining_dst_size -= extra_bytes;
 
             wrote_sei = 1;
         }
 
c5a5ca75
         src_offset += curr_src_len;
3af71ac3
         dst_data += curr_dst_len;
 
         remaining_src_size -= curr_src_len;
         remaining_dst_size -= curr_dst_len;
     }
 
     return 0;
 }
 
9875695e
 /**
  * Returns a sufficient number of bytes to contain the sei data.
  * It may be greater than the minimum required.
  */
 static int get_sei_msg_bytes(const ExtraSEI* sei, int type){
     int copied_size;
     if (sei->size == 0)
         return 0;
 
     copied_size = -copy_emulation_prev(sei->data,
                                        sei->size,
                                        NULL,
                                        0,
                                        0);
 
     if ((sei->size % 255) == 0) //may result in an extra byte
         copied_size++;
 
     return copied_size + sei->size / 255 + 1 + type / 255 + 1;
 }
 
3af71ac3
 static int vtenc_cm_to_avpacket(
     AVCodecContext    *avctx,
     CMSampleBufferRef sample_buffer,
9ea91e41
     AVPacket          *pkt,
     ExtraSEI          *sei)
3af71ac3
 {
     VTEncContext *vtctx = avctx->priv_data;
 
     int     status;
     bool    is_key_frame;
     bool    add_header;
     size_t  length_code_size;
     size_t  header_size = 0;
     size_t  in_buf_size;
c5a5ca75
     size_t  out_buf_size;
9ea91e41
     size_t  sei_nalu_size = 0;
3af71ac3
     int64_t dts_delta;
     int64_t time_base_num;
c5a5ca75
     int nalu_count;
3af71ac3
     CMTime  pts;
     CMTime  dts;
     CMVideoFormatDescriptionRef vid_fmt;
 
 
     vtenc_get_frame_info(sample_buffer, &is_key_frame);
     status = get_length_code_size(avctx, sample_buffer, &length_code_size);
     if (status) return status;
 
     add_header = is_key_frame && !(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER);
 
     if (add_header) {
         vid_fmt = CMSampleBufferGetFormatDescription(sample_buffer);
         if (!vid_fmt) {
             av_log(avctx, AV_LOG_ERROR, "Cannot get format description.\n");
c5a5ca75
             return AVERROR_EXTERNAL;
3af71ac3
         }
 
         int status = get_params_size(avctx, vid_fmt, &header_size);
         if (status) return status;
     }
 
c5a5ca75
     status = count_nalus(length_code_size, sample_buffer, &nalu_count);
     if(status)
         return status;
3af71ac3
 
9ea91e41
     if (sei) {
9875695e
         size_t msg_size = get_sei_msg_bytes(sei,
                                             SEI_TYPE_USER_DATA_REGISTERED);
 
         sei_nalu_size = sizeof(start_code) + 1 + msg_size + 1;
9ea91e41
     }
 
c5a5ca75
     in_buf_size = CMSampleBufferGetTotalSampleSize(sample_buffer);
     out_buf_size = header_size +
                    in_buf_size +
9ea91e41
                    sei_nalu_size +
c5a5ca75
                    nalu_count * ((int)sizeof(start_code) - (int)length_code_size);
3af71ac3
 
c5a5ca75
     status = ff_alloc_packet2(avctx, pkt, out_buf_size, out_buf_size);
     if (status < 0)
         return status;
3af71ac3
 
c5a5ca75
     if (add_header) {
         status = copy_param_sets(avctx, vid_fmt, pkt->data, out_buf_size);
         if(status) return status;
     }
3af71ac3
 
c5a5ca75
     status = copy_replace_length_codes(
         avctx,
         length_code_size,
         sample_buffer,
9875695e
         sei,
c5a5ca75
         pkt->data + header_size,
9875695e
         pkt->size - header_size
c5a5ca75
     );
3af71ac3
 
c5a5ca75
     if (status) {
d3874b74
         av_log(avctx, AV_LOG_ERROR, "Error copying packet data: %d\n", status);
c5a5ca75
         return status;
3af71ac3
     }
 
     if (is_key_frame) {
         pkt->flags |= AV_PKT_FLAG_KEY;
     }
 
     pts = CMSampleBufferGetPresentationTimeStamp(sample_buffer);
     dts = CMSampleBufferGetDecodeTimeStamp      (sample_buffer);
 
2fbf3938
     if (CMTIME_IS_INVALID(dts)) {
         if (!vtctx->has_b_frames) {
             dts = pts;
         } else {
             av_log(avctx, AV_LOG_ERROR, "DTS is invalid.\n");
             return AVERROR_EXTERNAL;
         }
     }
 
3af71ac3
     dts_delta = vtctx->dts_delta >= 0 ? vtctx->dts_delta : 0;
     time_base_num = avctx->time_base.num;
     pkt->pts = pts.value / time_base_num;
     pkt->dts = dts.value / time_base_num - dts_delta;
c5a5ca75
     pkt->size = out_buf_size;
3af71ac3
 
     return 0;
 }
 
 /*
  * contiguous_buf_size is 0 if not contiguous, and the size of the buffer
  * containing all planes if so.
  */
 static int get_cv_pixel_info(
     AVCodecContext *avctx,
     const AVFrame  *frame,
     int            *color,
     int            *plane_count,
     size_t         *widths,
     size_t         *heights,
     size_t         *strides,
     size_t         *contiguous_buf_size)
 {
     VTEncContext *vtctx = avctx->priv_data;
     int av_format       = frame->format;
     int av_color_range  = av_frame_get_color_range(frame);
     int i;
d7cf3610
     int range_guessed;
     int status;
3af71ac3
 
d7cf3610
     status = get_cv_pixel_format(avctx, av_format, av_color_range, color, &range_guessed);
     if (status) {
         av_log(avctx,
             AV_LOG_ERROR,
             "Could not get pixel format for color format '%s' range '%s'.\n",
             av_get_pix_fmt_name(av_format),
             av_color_range > AVCOL_RANGE_UNSPECIFIED &&
             av_color_range < AVCOL_RANGE_NB ?
                av_color_range_name(av_color_range) :
                "Unknown");
3af71ac3
 
d7cf3610
         return AVERROR(EINVAL);
     }
3af71ac3
 
d7cf3610
     if (range_guessed) {
         if (!vtctx->warned_color_range) {
             vtctx->warned_color_range = true;
             av_log(avctx,
                    AV_LOG_WARNING,
                    "Color range not set for %s. Using MPEG range.\n",
                    av_get_pix_fmt_name(av_format));
3af71ac3
         }
 
d7cf3610
         av_log(avctx, AV_LOG_WARNING, "");
     }
 
     switch (av_format) {
     case AV_PIX_FMT_NV12:
3af71ac3
         *plane_count = 2;
 
         widths [0] = avctx->width;
         heights[0] = avctx->height;
         strides[0] = frame ? frame->linesize[0] : avctx->width;
 
         widths [1] = (avctx->width  + 1) / 2;
         heights[1] = (avctx->height + 1) / 2;
         strides[1] = frame ? frame->linesize[1] : (avctx->width + 1) & -2;
         break;
 
     case AV_PIX_FMT_YUV420P:
         *plane_count = 3;
 
         widths [0] = avctx->width;
         heights[0] = avctx->height;
         strides[0] = frame ? frame->linesize[0] : avctx->width;
 
         widths [1] = (avctx->width  + 1) / 2;
         heights[1] = (avctx->height + 1) / 2;
         strides[1] = frame ? frame->linesize[1] : (avctx->width + 1) / 2;
 
         widths [2] = (avctx->width  + 1) / 2;
         heights[2] = (avctx->height + 1) / 2;
         strides[2] = frame ? frame->linesize[2] : (avctx->width + 1) / 2;
         break;
 
d7cf3610
     default:
         av_log(
                avctx,
                AV_LOG_ERROR,
                "Could not get frame format info for color %d range %d.\n",
                av_format,
                av_color_range);
 
         return AVERROR(EINVAL);
3af71ac3
     }
 
     *contiguous_buf_size = 0;
     for (i = 0; i < *plane_count; i++) {
         if (i < *plane_count - 1 &&
             frame->data[i] + strides[i] * heights[i] != frame->data[i + 1]) {
             *contiguous_buf_size = 0;
             break;
         }
 
         *contiguous_buf_size += strides[i] * heights[i];
     }
 
     return 0;
 }
 
 #if !TARGET_OS_IPHONE
 //Not used on iOS - frame is always copied.
 static void free_avframe(
ecba35bb
     void       *release_ctx,
     const void *data,
     size_t      size,
     size_t      plane_count,
     const void *plane_addresses[])
3af71ac3
 {
     AVFrame *frame = release_ctx;
     av_frame_free(&frame);
 }
 #else
 //Not used on OSX - frame is never copied.
 static int copy_avframe_to_pixel_buffer(AVCodecContext   *avctx,
                                         const AVFrame    *frame,
                                         CVPixelBufferRef cv_img,
                                         const size_t     *plane_strides,
                                         const size_t     *plane_rows)
 {
     int i, j;
     size_t plane_count;
     int status;
     int rows;
     int src_stride;
     int dst_stride;
     uint8_t *src_addr;
     uint8_t *dst_addr;
     size_t copy_bytes;
 
     status = CVPixelBufferLockBaseAddress(cv_img, 0);
     if (status) {
         av_log(
             avctx,
             AV_LOG_ERROR,
             "Error: Could not lock base address of CVPixelBuffer: %d.\n",
             status
         );
     }
 
     if (CVPixelBufferIsPlanar(cv_img)) {
         plane_count = CVPixelBufferGetPlaneCount(cv_img);
         for (i = 0; frame->data[i]; i++) {
             if (i == plane_count) {
                 CVPixelBufferUnlockBaseAddress(cv_img, 0);
                 av_log(avctx,
                     AV_LOG_ERROR,
                     "Error: different number of planes in AVFrame and CVPixelBuffer.\n"
                 );
 
                 return AVERROR_EXTERNAL;
             }
 
             dst_addr = (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(cv_img, i);
             src_addr = (uint8_t*)frame->data[i];
             dst_stride = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i);
             src_stride = plane_strides[i];
             rows = plane_rows[i];
 
             if (dst_stride == src_stride) {
                 memcpy(dst_addr, src_addr, src_stride * rows);
             } else {
                 copy_bytes = dst_stride < src_stride ? dst_stride : src_stride;
 
                 for (j = 0; j < rows; j++) {
                     memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes);
                 }
             }
         }
     } else {
         if (frame->data[1]) {
             CVPixelBufferUnlockBaseAddress(cv_img, 0);
             av_log(avctx,
                 AV_LOG_ERROR,
                 "Error: different number of planes in AVFrame and non-planar CVPixelBuffer.\n"
             );
 
             return AVERROR_EXTERNAL;
         }
 
         dst_addr = (uint8_t*)CVPixelBufferGetBaseAddress(cv_img);
         src_addr = (uint8_t*)frame->data[0];
         dst_stride = CVPixelBufferGetBytesPerRow(cv_img);
         src_stride = plane_strides[0];
         rows = plane_rows[0];
 
         if (dst_stride == src_stride) {
             memcpy(dst_addr, src_addr, src_stride * rows);
         } else {
             copy_bytes = dst_stride < src_stride ? dst_stride : src_stride;
 
             for (j = 0; j < rows; j++) {
                 memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes);
             }
         }
     }
 
     status = CVPixelBufferUnlockBaseAddress(cv_img, 0);
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error: Could not unlock CVPixelBuffer base address: %d.\n", status);
         return AVERROR_EXTERNAL;
     }
 
     return 0;
 }
 #endif //!TARGET_OS_IPHONE
 
 static int create_cv_pixel_buffer(AVCodecContext   *avctx,
                                   const AVFrame    *frame,
                                   CVPixelBufferRef *cv_img)
 {
     int plane_count;
     int color;
     size_t widths [AV_NUM_DATA_POINTERS];
     size_t heights[AV_NUM_DATA_POINTERS];
     size_t strides[AV_NUM_DATA_POINTERS];
     int status;
     size_t contiguous_buf_size;
dc750194
 #if TARGET_OS_IPHONE
d7cf3610
     CVPixelBufferPoolRef pix_buf_pool;
     VTEncContext* vtctx = avctx->priv_data;
dc750194
 #else
     CFMutableDictionaryRef pix_buf_attachments = CFDictionaryCreateMutable(
                                                    kCFAllocatorDefault,
                                                    10,
                                                    &kCFCopyStringDictionaryKeyCallBacks,
                                                    &kCFTypeDictionaryValueCallBacks);
3af71ac3
 
dc750194
     if (!pix_buf_attachments) return AVERROR(ENOMEM);
 #endif
0d4bf307
 
     if (avctx->pix_fmt == AV_PIX_FMT_VIDEOTOOLBOX) {
         av_assert0(frame->format == AV_PIX_FMT_VIDEOTOOLBOX);
 
         *cv_img = (CVPixelBufferRef)frame->data[3];
         av_assert0(*cv_img);
 
         CFRetain(*cv_img);
         return 0;
     }
 
3af71ac3
     memset(widths,  0, sizeof(widths));
     memset(heights, 0, sizeof(heights));
     memset(strides, 0, sizeof(strides));
 
     status = get_cv_pixel_info(
         avctx,
         frame,
         &color,
         &plane_count,
         widths,
         heights,
         strides,
         &contiguous_buf_size
     );
 
     if (status) {
         av_log(
             avctx,
             AV_LOG_ERROR,
             "Error: Cannot convert format %d color_range %d: %d\n",
             frame->format,
             av_frame_get_color_range(frame),
             status
         );
 
         return AVERROR_EXTERNAL;
     }
 
 #if TARGET_OS_IPHONE
d7cf3610
     pix_buf_pool = VTCompressionSessionGetPixelBufferPool(vtctx->session);
     if (!pix_buf_pool) {
         av_log(avctx, AV_LOG_ERROR, "Could not get pixel buffer pool.\n");
         return AVERROR_EXTERNAL;
     }
 
     status = CVPixelBufferPoolCreatePixelBuffer(NULL,
                                                 pix_buf_pool,
                                                 cv_img);
 
3af71ac3
 
     if (status) {
d7cf3610
         av_log(avctx, AV_LOG_ERROR, "Could not create pixel buffer from pool: %d.\n", status);
3af71ac3
         return AVERROR_EXTERNAL;
     }
 
     status = copy_avframe_to_pixel_buffer(avctx, frame, *cv_img, strides, heights);
     if (status) {
         CFRelease(*cv_img);
         *cv_img = NULL;
         return status;
     }
 #else
     AVFrame *enc_frame = av_frame_alloc();
     if (!enc_frame) return AVERROR(ENOMEM);
 
     status = av_frame_ref(enc_frame, frame);
     if (status) {
         av_frame_free(&enc_frame);
         return status;
     }
 
     status = CVPixelBufferCreateWithPlanarBytes(
         kCFAllocatorDefault,
         enc_frame->width,
         enc_frame->height,
         color,
         NULL,
         contiguous_buf_size,
         plane_count,
         (void **)enc_frame->data,
         widths,
         heights,
         strides,
         free_avframe,
         enc_frame,
         NULL,
         cv_img
     );
 
dc750194
     add_color_attr(avctx, pix_buf_attachments);
     CVBufferSetAttachments(*cv_img, pix_buf_attachments, kCVAttachmentMode_ShouldPropagate);
     CFRelease(pix_buf_attachments);
 
3af71ac3
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error: Could not create CVPixelBuffer: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 #endif
 
     return 0;
 }
 
9d8a38d2
 static int create_encoder_dict_h264(const AVFrame *frame,
                                     CFDictionaryRef* dict_out)
 {
     CFDictionaryRef dict = NULL;
     if (frame->pict_type == AV_PICTURE_TYPE_I) {
         const void *keys[] = { kVTEncodeFrameOptionKey_ForceKeyFrame };
         const void *vals[] = { kCFBooleanTrue };
 
         dict = CFDictionaryCreate(NULL, keys, vals, 1, NULL, NULL);
         if(!dict) return AVERROR(ENOMEM);
     }
 
     *dict_out = dict;
     return 0;
 }
 
3af71ac3
 static int vtenc_send_frame(AVCodecContext *avctx,
                             VTEncContext   *vtctx,
                             const AVFrame  *frame)
 {
     CMTime time;
9d8a38d2
     CFDictionaryRef frame_dict;
3af71ac3
     CVPixelBufferRef cv_img = NULL;
f45d5e07
     AVFrameSideData *side_data = NULL;
9ea91e41
     ExtraSEI *sei = NULL;
3af71ac3
     int status = create_cv_pixel_buffer(avctx, frame, &cv_img);
 
     if (status) return status;
 
9d8a38d2
     status = create_encoder_dict_h264(frame, &frame_dict);
     if (status) {
         CFRelease(cv_img);
         return status;
     }
 
f45d5e07
     side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC);
     if (vtctx->a53_cc && side_data && side_data->size) {
9ea91e41
         sei = av_mallocz(sizeof(*sei));
         if (!sei) {
             av_log(avctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n");
         } else {
             int ret = ff_alloc_a53_sei(frame, 0, &sei->data, &sei->size);
             if (ret < 0) {
                 av_log(avctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n");
                 av_free(sei);
                 sei = NULL;
             }
         }
     }
 
3af71ac3
     time = CMTimeMake(frame->pts * avctx->time_base.num, avctx->time_base.den);
     status = VTCompressionSessionEncodeFrame(
         vtctx->session,
         cv_img,
         time,
         kCMTimeInvalid,
9d8a38d2
         frame_dict,
9ea91e41
         sei,
3af71ac3
         NULL
     );
 
9d8a38d2
     if (frame_dict) CFRelease(frame_dict);
3af71ac3
     CFRelease(cv_img);
 
     if (status) {
         av_log(avctx, AV_LOG_ERROR, "Error: cannot encode frame: %d\n", status);
         return AVERROR_EXTERNAL;
     }
 
     return 0;
 }
 
 static av_cold int vtenc_frame(
     AVCodecContext *avctx,
     AVPacket       *pkt,
     const AVFrame  *frame,
     int            *got_packet)
 {
     VTEncContext *vtctx = avctx->priv_data;
     bool get_frame;
     int status;
     CMSampleBufferRef buf = NULL;
9ea91e41
     ExtraSEI *sei = NULL;
3af71ac3
 
     if (frame) {
         status = vtenc_send_frame(avctx, vtctx, frame);
 
         if (status) {
             status = AVERROR_EXTERNAL;
             goto end_nopkt;
         }
 
         if (vtctx->frame_ct_in == 0) {
             vtctx->first_pts = frame->pts;
         } else if(vtctx->frame_ct_in == 1 && vtctx->has_b_frames) {
             vtctx->dts_delta = frame->pts - vtctx->first_pts;
         }
 
         vtctx->frame_ct_in++;
     } else if(!vtctx->flushing) {
         vtctx->flushing = true;
 
         status = VTCompressionSessionCompleteFrames(vtctx->session,
                                                     kCMTimeIndefinite);
 
         if (status) {
             av_log(avctx, AV_LOG_ERROR, "Error flushing frames: %d\n", status);
             status = AVERROR_EXTERNAL;
             goto end_nopkt;
         }
     }
 
     *got_packet = 0;
     get_frame = vtctx->dts_delta >= 0 || !frame;
     if (!get_frame) {
         status = 0;
         goto end_nopkt;
     }
 
9ea91e41
     status = vtenc_q_pop(vtctx, !frame, &buf, &sei);
3af71ac3
     if (status) goto end_nopkt;
     if (!buf)   goto end_nopkt;
 
9ea91e41
     status = vtenc_cm_to_avpacket(avctx, buf, pkt, sei);
     if (sei) {
         if (sei->data) av_free(sei->data);
         av_free(sei);
     }
3af71ac3
     CFRelease(buf);
     if (status) goto end_nopkt;
 
     *got_packet = 1;
     return 0;
 
 end_nopkt:
     av_packet_unref(pkt);
     return status;
 }
 
98a3355c
 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;
 
9ea91e41
     status = vtenc_q_pop(vtctx, 0, &buf, NULL);
98a3355c
     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;
 }
 
3af71ac3
 static av_cold int vtenc_close(AVCodecContext *avctx)
 {
     VTEncContext *vtctx = avctx->priv_data;
 
     if(!vtctx->session) return 0;
 
aa413b81
     VTCompressionSessionCompleteFrames(vtctx->session,
                                        kCMTimeIndefinite);
     clear_frame_queue(vtctx);
3af71ac3
     pthread_cond_destroy(&vtctx->cv_sample_sent);
     pthread_mutex_destroy(&vtctx->lock);
     CFRelease(vtctx->session);
     vtctx->session = NULL;
 
dc750194
     if (vtctx->color_primaries) {
         CFRelease(vtctx->color_primaries);
         vtctx->color_primaries = NULL;
     }
 
     if (vtctx->transfer_function) {
         CFRelease(vtctx->transfer_function);
         vtctx->transfer_function = NULL;
     }
 
     if (vtctx->ycbcr_matrix) {
         CFRelease(vtctx->ycbcr_matrix);
         vtctx->ycbcr_matrix = NULL;
     }
 
3af71ac3
     return 0;
 }
 
 static const enum AVPixelFormat pix_fmts[] = {
0d4bf307
     AV_PIX_FMT_VIDEOTOOLBOX,
3af71ac3
     AV_PIX_FMT_NV12,
     AV_PIX_FMT_YUV420P,
     AV_PIX_FMT_NONE
 };
 
 #define OFFSET(x) offsetof(VTEncContext, x)
 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
     { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = H264_PROF_AUTO }, H264_PROF_AUTO, H264_PROF_COUNT, VE, "profile" },
     { "baseline", "Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_BASELINE }, INT_MIN, INT_MAX, VE, "profile" },
     { "main",     "Main Profile",     0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_MAIN     }, INT_MIN, INT_MAX, VE, "profile" },
     { "high",     "High Profile",     0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_HIGH     }, INT_MIN, INT_MAX, VE, "profile" },
 
     { "level", "Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 52, VE, "level" },
     { "1.3", "Level 1.3, only available with Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, INT_MIN, INT_MAX, VE, "level" },
     { "3.0", "Level 3.0", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, INT_MIN, INT_MAX, VE, "level" },
     { "3.1", "Level 3.1", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, INT_MIN, INT_MAX, VE, "level" },
     { "3.2", "Level 3.2", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, INT_MIN, INT_MAX, VE, "level" },
     { "4.0", "Level 4.0", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, INT_MIN, INT_MAX, VE, "level" },
     { "4.1", "Level 4.1", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, INT_MIN, INT_MAX, VE, "level" },
     { "4.2", "Level 4.2", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, INT_MIN, INT_MAX, VE, "level" },
     { "5.0", "Level 5.0", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, INT_MIN, INT_MAX, VE, "level" },
     { "5.1", "Level 5.1", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, INT_MIN, INT_MAX, VE, "level" },
     { "5.2", "Level 5.2", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, INT_MIN, INT_MAX, VE, "level" },
 
fbe0cf8c
     { "allow_sw", "Allow software encoding", OFFSET(allow_sw), AV_OPT_TYPE_BOOL,
         { .i64 = 0 }, 0, 1, VE },
 
645df431
     { "coder", "Entropy coding", OFFSET(entropy), AV_OPT_TYPE_INT, { .i64 = VT_ENTROPY_NOT_SET }, VT_ENTROPY_NOT_SET, VT_CABAC, VE, "coder" },
     { "cavlc", "CAVLC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CAVLC }, INT_MIN, INT_MAX, VE, "coder" },
     { "vlc",   "CAVLC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CAVLC }, INT_MIN, INT_MAX, VE, "coder" },
     { "cabac", "CABAC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CABAC }, INT_MIN, INT_MAX, VE, "coder" },
     { "ac",    "CABAC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CABAC }, INT_MIN, INT_MAX, VE, "coder" },
 
ca429e72
     { "realtime", "Hint that encoding should happen in real-time if not faster (e.g. capturing from camera).",
         OFFSET(realtime), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
 
4b806081
     { "frames_before", "Other frames will come before the frames in this session. This helps smooth concatenation issues.",
         OFFSET(frames_before), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
     { "frames_after", "Other frames will come after the frames in this session. This helps smooth concatenation issues.",
         OFFSET(frames_after), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
 
f04c27fe
     { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VE },
9ea91e41
 
3af71ac3
     { NULL },
 };
 
 static const AVClass h264_videotoolbox_class = {
     .class_name = "h264_videotoolbox",
     .item_name  = av_default_item_name,
     .option     = options,
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
 AVCodec ff_h264_videotoolbox_encoder = {
     .name             = "h264_videotoolbox",
     .long_name        = NULL_IF_CONFIG_SMALL("VideoToolbox H.264 Encoder"),
     .type             = AVMEDIA_TYPE_VIDEO,
     .id               = AV_CODEC_ID_H264,
     .priv_data_size   = sizeof(VTEncContext),
     .pix_fmts         = pix_fmts,
     .init             = vtenc_init,
     .encode2          = vtenc_frame,
     .close            = vtenc_close,
     .capabilities     = AV_CODEC_CAP_DELAY,
     .priv_class       = &h264_videotoolbox_class,
     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
                         FF_CODEC_CAP_INIT_CLEANUP,
 };