videotoolbox: fallback to I420 only if possible
[vlc.git] / modules / codec / videotoolbox.m
blobb00eb467acf1731501504a14c2d95d8654cc1e4f
1 /*****************************************************************************
2  * videotoolbox.m: Video Toolbox decoder
3  *****************************************************************************
4  * Copyright © 2014-2015 VideoLabs SAS
5  *
6  * Authors: Felix Paul Kühne <fkuehne # videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
23 #pragma mark preamble
25 #ifdef HAVE_CONFIG_H
26 # import "config.h"
27 #endif
29 #import <vlc_common.h>
30 #import <vlc_plugin.h>
31 #import <vlc_codec.h>
32 #import "hxxx_helper.h"
33 #import <vlc_bits.h>
34 #import <vlc_boxes.h>
35 #import "vt_utils.h"
36 #import "../packetizer/h264_nal.h"
37 #import "../packetizer/h264_slice.h"
38 #import "../packetizer/hxxx_nal.h"
39 #import "../packetizer/hxxx_sei.h"
41 #import <VideoToolbox/VideoToolbox.h>
42 #import <VideoToolbox/VTErrors.h>
44 #import <Foundation/Foundation.h>
45 #import <TargetConditionals.h>
47 #import <sys/types.h>
48 #import <sys/sysctl.h>
49 #import <mach/machine.h>
51 #define ALIGN_16( x ) ( ( ( x ) + 15 ) / 16 * 16 )
52 #define VT_RESTART_MAX 1
54 #if TARGET_OS_IPHONE
55 #import <UIKit/UIKit.h>
57 /* support iOS SDKs < v9.1 */
58 #ifndef CPUFAMILY_ARM_TWISTER
59 #define CPUFAMILY_ARM_TWISTER 0x92fb37c8
60 #endif
62 #endif
64 // Define stuff for older SDKs
65 #if (TARGET_OS_OSX && MAC_OS_X_VERSION_MAX_ALLOWED < 101100) || \
66     (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < 90000) || \
67     (TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED < 90000)
68 enum { kCMVideoCodecType_HEVC = 'hvc1' };
69 #endif
71 #if (!TARGET_OS_OSX || MAC_OS_X_VERSION_MAX_ALLOWED < 1090)
72 const CFStringRef kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder = CFSTR("EnableHardwareAcceleratedVideoDecoder");
73 const CFStringRef kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder = CFSTR("RequireHardwareAcceleratedVideoDecoder");
74 #endif
76 #pragma mark - module descriptor
78 static int OpenDecoder(vlc_object_t *);
79 static void CloseDecoder(vlc_object_t *);
81 #define VT_REQUIRE_HW_DEC N_("Use Hardware decoders only")
82 #define VT_FORCE_CVPX_CHROMA "Force the VT decoder CVPX chroma"
83 #define VT_FORCE_CVPX_CHROMA_LONG "Values can be 'BGRA', 'y420', '420f', '420v', '2vuy'. \
84     By Default, the best chroma is choosen by the VT decoder."
86 vlc_module_begin()
87 set_category(CAT_INPUT)
88 set_subcategory(SUBCAT_INPUT_VCODEC)
89 set_description(N_("VideoToolbox video decoder"))
90 set_capability("video decoder",800)
91 set_callbacks(OpenDecoder, CloseDecoder)
93 add_obsolete_bool("videotoolbox-temporal-deinterlacing")
94 add_bool("videotoolbox-hw-decoder-only", true, VT_REQUIRE_HW_DEC, VT_REQUIRE_HW_DEC, false)
95 add_string("videotoolbox-cvpx-chroma", "", VT_FORCE_CVPX_CHROMA, VT_FORCE_CVPX_CHROMA_LONG, true);
96 vlc_module_end()
98 #pragma mark - local prototypes
100 enum vtsession_status
102     VTSESSION_STATUS_OK,
103     VTSESSION_STATUS_RESTART,
104     VTSESSION_STATUS_ABORT,
107 static int ConfigureVout(decoder_t *);
108 static CFMutableDictionaryRef ESDSExtradataInfoCreate(decoder_t *, uint8_t *, uint32_t);
109 static CFMutableDictionaryRef ExtradataInfoCreate(CFStringRef, void *, size_t);
110 static CFMutableDictionaryRef CreateSessionDescriptionFormat(decoder_t *, unsigned, unsigned);
111 static int HandleVTStatus(decoder_t *, OSStatus, enum vtsession_status *);
112 static int DecodeBlock(decoder_t *, block_t *);
113 static void RequestFlush(decoder_t *);
114 static void Drain(decoder_t *p_dec, bool flush);
115 static void DecoderCallback(void *, void *, OSStatus, VTDecodeInfoFlags,
116                             CVPixelBufferRef, CMTime, CMTime);
117 static BOOL deviceSupportsHEVC();
118 static BOOL deviceSupportsAdvancedProfiles();
119 static BOOL deviceSupportsAdvancedLevels();
121 typedef struct frame_info_t frame_info_t;
123 struct frame_info_t
125     picture_t *p_picture;
126     int i_poc;
127     int i_foc;
128     bool b_forced;
129     bool b_flush;
130     bool b_keyframe;
131     bool b_field;
132     bool b_progressive;
133     bool b_top_field_first;
134     uint8_t i_num_ts;
135     unsigned i_length;
136     frame_info_t *p_next;
139 #pragma mark - decoder structure
141 #define H264_MAX_DPB 16
142 #define VT_MAX_SEI_COUNT 16
144 struct decoder_sys_t
146     CMVideoCodecType            codec;
147     struct                      hxxx_helper hh;
149     /* Codec specific callbacks */
150     bool                        (*pf_codec_init)(decoder_t *);
151     void                        (*pf_codec_clean)(decoder_t *);
152     bool                        (*pf_codec_supported)(decoder_t *);
153     bool                        (*pf_late_start)(decoder_t *);
154     block_t*                    (*pf_process_block)(decoder_t *,
155                                                     block_t *, bool *);
156     bool                        (*pf_need_restart)(decoder_t *,
157                                                    VTDecompressionSessionRef);
158     bool                        (*pf_configure_vout)(decoder_t *);
159     CFMutableDictionaryRef      (*pf_get_extradata)(decoder_t *);
160     bool                        (*pf_fill_reorder_info)(decoder_t *, const block_t *,
161                                                         frame_info_t *);
162     /* !Codec specific callbacks */
164     bool                        b_vt_feed;
165     bool                        b_vt_flush;
166     VTDecompressionSessionRef   session;
167     CMVideoFormatDescriptionRef videoFormatDescription;
169     vlc_mutex_t                 lock;
170     frame_info_t               *p_pic_reorder;
171     uint8_t                     i_pic_reorder;
172     uint8_t                     i_pic_reorder_max;
173     bool                        b_invalid_pic_reorder_max;
174     bool                        b_poc_based_reorder;
176     bool                        b_format_propagated;
178     enum vtsession_status       vtsession_status;
179     unsigned                    i_restart_count;
181     int                         i_cvpx_format;
182     bool                        b_cvpx_format_forced;
184     h264_poc_context_t          h264_pocctx;
185     hevc_poc_ctx_t              hevc_pocctx;
186     bool                        b_drop_blocks;
187     date_t                      pts;
189     struct pic_holder          *pic_holder;
192 struct pic_holder
194     bool        closed;
195     vlc_mutex_t lock;
196     vlc_cond_t  wait;
197     uint8_t     nb_field_out;
198     uint8_t     field_reorder_max;
201 static void pic_holder_update_reorder_max(struct pic_holder *, uint8_t, uint8_t);
203 #pragma mark - start & stop
205 /* Codec Specific */
207 static void HXXXGetBestChroma(decoder_t *p_dec)
209     decoder_sys_t *p_sys = p_dec->p_sys;
211     if (p_sys->i_cvpx_format != 0)
212         return;
214     uint8_t i_chroma_format, i_depth_luma, i_depth_chroma;
215     if (hxxx_helper_get_chroma_chroma(&p_sys->hh, &i_chroma_format, &i_depth_luma,
216                                       &i_depth_chroma) != VLC_SUCCESS)
217         return;
219     if (i_chroma_format == 1 /* YUV 4:2:0 */)
220     {
221         if (i_depth_luma == 8 && i_depth_chroma == 8)
222             p_sys->i_cvpx_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
223 #if !TARGET_OS_IPHONE
224         /* Not for iOS since there is no 10bits textures with the old iOS
225          * openGLES version, and therefore no P010 shaders */
226         else if (i_depth_luma == 10 && i_depth_chroma == 10 && deviceSupportsHEVC())
227             p_sys->i_cvpx_format = 'x420'; /* kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange */
228 #endif
229     }
232 static void GetxPSH264(uint8_t i_pps_id, void *priv,
233                       const h264_sequence_parameter_set_t **pp_sps,
234                       const h264_picture_parameter_set_t **pp_pps)
236     decoder_sys_t *p_sys = priv;
238     *pp_pps = p_sys->hh.h264.pps_list[i_pps_id].h264_pps;
239     if(*pp_pps == NULL)
240         *pp_sps = NULL;
241     else
242         *pp_sps = p_sys->hh.h264.sps_list[(*pp_pps)->i_sps_id].h264_sps;
245 struct sei_callback_h264_s
247     uint8_t i_pic_struct;
248     const h264_sequence_parameter_set_t *p_sps;
251 static bool ParseH264SEI(const hxxx_sei_data_t *p_sei_data, void *priv)
253     if(p_sei_data->i_type == HXXX_SEI_PIC_TIMING)
254     {
255         struct sei_callback_h264_s *s = priv;
256         if(s->p_sps && s->p_sps->vui.b_valid)
257         {
258             if(s->p_sps->vui.b_hrd_parameters_present_flag)
259             {
260                 bs_read(p_sei_data->p_bs, s->p_sps->vui.i_cpb_removal_delay_length_minus1 + 1);
261                 bs_read(p_sei_data->p_bs, s->p_sps->vui.i_dpb_output_delay_length_minus1 + 1);
262             }
264             if(s->p_sps->vui.b_pic_struct_present_flag)
265                s->i_pic_struct = bs_read( p_sei_data->p_bs, 4);
266         }
267         return false;
268     }
270     return true;
273 static bool FillReorderInfoH264(decoder_t *p_dec, const block_t *p_block,
274                                 frame_info_t *p_info)
276     decoder_sys_t *p_sys = p_dec->p_sys;
277     hxxx_iterator_ctx_t itctx;
278     hxxx_iterator_init(&itctx, p_block->p_buffer, p_block->i_buffer,
279                        p_sys->hh.i_nal_length_size);
281     const uint8_t *p_nal; size_t i_nal;
282     struct
283     {
284         const uint8_t *p_nal;
285         size_t i_nal;
286     } sei_array[VT_MAX_SEI_COUNT];
287     size_t i_sei_count = 0;
288     while(hxxx_iterate_next(&itctx, &p_nal, &i_nal))
289     {
290         if(i_nal < 2)
291             continue;
293         const enum h264_nal_unit_type_e i_nal_type = p_nal[0] & 0x1F;
295         if (i_nal_type <= H264_NAL_SLICE_IDR && i_nal_type != H264_NAL_UNKNOWN)
296         {
297             h264_slice_t slice;
298             if(!h264_decode_slice(p_nal, i_nal, GetxPSH264, p_sys, &slice))
299                 return false;
301             const h264_sequence_parameter_set_t *p_sps;
302             const h264_picture_parameter_set_t *p_pps;
303             GetxPSH264(slice.i_pic_parameter_set_id, p_sys, &p_sps, &p_pps);
304             if(p_sps)
305             {
306                 int bFOC;
307                 h264_compute_poc(p_sps, &slice, &p_sys->h264_pocctx,
308                                  &p_info->i_poc, &p_info->i_foc, &bFOC);
310                 p_info->b_keyframe = slice.type == H264_SLICE_TYPE_I;
311                 p_info->b_flush = (slice.type == H264_SLICE_TYPE_I) || slice.has_mmco5;
312                 p_info->b_field = slice.i_field_pic_flag;
313                 p_info->b_progressive = !p_sps->mb_adaptive_frame_field_flag &&
314                                         !slice.i_field_pic_flag;
316                 struct sei_callback_h264_s sei;
317                 sei.p_sps = p_sps;
318                 sei.i_pic_struct = UINT8_MAX;
320                 for(size_t i=0; i<i_sei_count; i++)
321                     HxxxParseSEI(sei_array[i].p_nal, sei_array[i].i_nal, 1,
322                                  ParseH264SEI, &sei);
324                 p_info->i_num_ts = h264_get_num_ts(p_sps, &slice, sei.i_pic_struct,
325                                                    p_info->i_foc, bFOC);
327                 if(!p_info->b_progressive)
328                     p_info->b_top_field_first = (sei.i_pic_struct % 2 == 1);
330                 /* Set frame rate for timings in case of missing rate */
331                 if( (!p_dec->fmt_in.video.i_frame_rate_base ||
332                      !p_dec->fmt_in.video.i_frame_rate) &&
333                     p_sps->vui.i_time_scale && p_sps->vui.i_num_units_in_tick )
334                 {
335                     date_Change( &p_sys->pts, p_sps->vui.i_time_scale,
336                                               p_sps->vui.i_num_units_in_tick );
337                 }
339                 if(!p_sys->b_invalid_pic_reorder_max && i_nal_type == H264_NAL_SLICE_IDR)
340                 {
341                     unsigned dummy;
342                     uint8_t i_reorder;
343                     h264_get_dpb_values(p_sps, &i_reorder, &dummy);
344                     vlc_mutex_lock(&p_sys->lock);
345                     p_sys->i_pic_reorder_max = i_reorder;
346                     pic_holder_update_reorder_max(p_sys->pic_holder,
347                                                   p_sys->i_pic_reorder_max,
348                                                   p_info->i_num_ts);
349                     vlc_mutex_unlock(&p_sys->lock);
350                 }
352             }
354             return true; /* No need to parse further NAL */
355         }
356         else if(i_nal_type == H264_NAL_SEI)
357         {
358             if(i_sei_count < VT_MAX_SEI_COUNT)
359             {
360                 sei_array[i_sei_count].p_nal = p_nal;
361                 sei_array[i_sei_count++].i_nal = i_nal;
362             }
363         }
364     }
366     return false;
370 static block_t *ProcessBlockH264(decoder_t *p_dec, block_t *p_block, bool *pb_config_changed)
372     decoder_sys_t *p_sys = p_dec->p_sys;
373     return p_sys->hh.pf_process_block(&p_sys->hh, p_block, pb_config_changed);
377 static bool InitH264(decoder_t *p_dec)
379     decoder_sys_t *p_sys = p_dec->p_sys;
380     h264_poc_context_init(&p_sys->h264_pocctx);
381     hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec),
382                      p_dec->fmt_in.i_codec, true);
383     return hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in.p_extra,
384                                              p_dec->fmt_in.i_extra) == VLC_SUCCESS;
387 static void CleanH264(decoder_t *p_dec)
389     decoder_sys_t *p_sys = p_dec->p_sys;
390     hxxx_helper_clean(&p_sys->hh);
393 static CFMutableDictionaryRef GetDecoderExtradataH264(decoder_t *p_dec)
395     decoder_sys_t *p_sys = p_dec->p_sys;
397     CFMutableDictionaryRef extradata = nil;
398     if (p_dec->fmt_in.i_extra && p_sys->hh.b_is_xvcC)
399     {
400         /* copy DecoderConfiguration */
401         extradata = ExtradataInfoCreate(CFSTR("avcC"),
402                                         p_dec->fmt_in.p_extra,
403                                         p_dec->fmt_in.i_extra);
404     }
405     else if (p_sys->hh.h264.i_pps_count && p_sys->hh.h264.i_sps_count)
406     {
407         /* build DecoderConfiguration from gathered */
408         block_t *p_avcC = h264_helper_get_avcc_config(&p_sys->hh);
409         if (p_avcC)
410         {
411             extradata = ExtradataInfoCreate(CFSTR("avcC"),
412                                             p_avcC->p_buffer,
413                                             p_avcC->i_buffer);
414             block_Release(p_avcC);
415         }
416     }
417     return extradata;
420 static bool CodecSupportedH264(decoder_t *p_dec)
422     decoder_sys_t *p_sys = p_dec->p_sys;
424     uint8_t i_profile, i_level;
425     if (hxxx_helper_get_current_profile_level(&p_sys->hh, &i_profile, &i_level))
426         return true;
428     switch (i_profile) {
429         case PROFILE_H264_BASELINE:
430         case PROFILE_H264_MAIN:
431         case PROFILE_H264_HIGH:
432             break;
434         case PROFILE_H264_HIGH_10:
435         {
436             if (deviceSupportsAdvancedProfiles())
437                 break;
438             else
439             {
440                 msg_Err(p_dec, "current device doesn't support H264 10bits");
441                 return false;
442             }
443         }
445         default:
446         {
447             msg_Warn(p_dec, "unknown H264 profile %" PRIx8, i_profile);
448             return false;
449         }
450     }
452     /* A level higher than 5.2 was not tested, so don't dare to try to decode
453      * it. On SoC A8, 4.2 is the highest specified profile. on Twister, we can
454      * do up to 5.2 */
455     if (i_level > 52 || (i_level > 42 && !deviceSupportsAdvancedLevels()))
456     {
457         msg_Err(p_dec, "current device doesn't support this H264 level: %"
458                 PRIx8, i_level);
459         return false;
460     }
462     HXXXGetBestChroma(p_dec);
464     return true;
467 static bool LateStartH264(decoder_t *p_dec)
469     decoder_sys_t *p_sys = p_dec->p_sys;
470     return (p_dec->fmt_in.i_extra == 0 &&
471             (!p_sys->hh.h264.i_pps_count || !p_sys->hh.h264.i_sps_count) );
474 static bool ConfigureVoutH264(decoder_t *p_dec)
476     decoder_sys_t *p_sys = p_dec->p_sys;
478     if(p_dec->fmt_in.video.primaries == COLOR_PRIMARIES_UNDEF)
479     {
480         video_color_primaries_t primaries;
481         video_transfer_func_t transfer;
482         video_color_space_t colorspace;
483         bool full_range;
484         if (hxxx_helper_get_colorimetry(&p_sys->hh,
485                                         &primaries,
486                                         &transfer,
487                                         &colorspace,
488                                         &full_range) == VLC_SUCCESS)
489         {
490             p_dec->fmt_out.video.primaries = primaries;
491             p_dec->fmt_out.video.transfer = transfer;
492             p_dec->fmt_out.video.space = colorspace;
493             p_dec->fmt_out.video.b_color_range_full = full_range;
494         }
495     }
497     if (!p_dec->fmt_in.video.i_visible_width || !p_dec->fmt_in.video.i_visible_height)
498     {
499         unsigned i_width, i_height, i_vis_width, i_vis_height;
500         if(VLC_SUCCESS ==
501            hxxx_helper_get_current_picture_size(&p_sys->hh,
502                                                 &i_width, &i_height,
503                                                 &i_vis_width, &i_vis_height))
504         {
505             p_dec->fmt_out.video.i_visible_width = i_vis_width;
506             p_dec->fmt_out.video.i_width = ALIGN_16( i_vis_width );
507             p_dec->fmt_out.video.i_visible_height = i_vis_height;
508             p_dec->fmt_out.video.i_height = ALIGN_16( i_vis_height );
509         }
510         else return false;
511     }
513     if(!p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den)
514     {
515         int i_sar_num, i_sar_den;
516         if (VLC_SUCCESS ==
517             hxxx_helper_get_current_sar(&p_sys->hh, &i_sar_num, &i_sar_den))
518         {
519             p_dec->fmt_out.video.i_sar_num = i_sar_num;
520             p_dec->fmt_out.video.i_sar_den = i_sar_den;
521         }
522     }
524     return true;
527 static bool VideoToolboxNeedsToRestartH264(decoder_t *p_dec,
528                                            VTDecompressionSessionRef session)
530     decoder_sys_t *p_sys = p_dec->p_sys;
531     const struct hxxx_helper *hh = &p_sys->hh;
533     unsigned w, h, vw, vh;
534     int sarn, sard;
536     if (hxxx_helper_get_current_picture_size(hh, &w, &h, &vw, &vh) != VLC_SUCCESS)
537         return true;
539     if (hxxx_helper_get_current_sar(hh, &sarn, &sard) != VLC_SUCCESS)
540         return true;
542     bool b_ret = true;
544     CFMutableDictionaryRef decoderConfiguration =
545             CreateSessionDescriptionFormat(p_dec, sarn, sard);
546     if (decoderConfiguration != nil)
547     {
548         CMFormatDescriptionRef newvideoFormatDesc;
549         /* create new video format description */
550         OSStatus status = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
551                                                          p_sys->codec,
552                                                          vw, vh,
553                                                          decoderConfiguration,
554                                                          &newvideoFormatDesc);
555         if (!status)
556         {
557             b_ret = !VTDecompressionSessionCanAcceptFormatDescription(session,
558                                                                       newvideoFormatDesc);
559             CFRelease(newvideoFormatDesc);
560         }
561         CFRelease(decoderConfiguration);
562     }
564     return b_ret;
567 static bool InitHEVC(decoder_t *p_dec)
569     decoder_sys_t *p_sys = p_dec->p_sys;
570     hevc_poc_cxt_init(&p_sys->hevc_pocctx);
571     hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec),
572                      p_dec->fmt_in.i_codec, true);
573     return hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in.p_extra,
574                                              p_dec->fmt_in.i_extra) == VLC_SUCCESS;
577 #define CleanHEVC CleanH264
579 static void GetxPSHEVC(uint8_t i_id, void *priv,
580                        hevc_picture_parameter_set_t **pp_pps,
581                        hevc_sequence_parameter_set_t **pp_sps,
582                        hevc_video_parameter_set_t **pp_vps)
584     decoder_sys_t *p_sys = priv;
586     *pp_pps = p_sys->hh.hevc.pps_list[i_id].hevc_pps;
587     if (*pp_pps == NULL)
588     {
589         *pp_vps = NULL;
590         *pp_sps = NULL;
591     }
592     else
593     {
594         uint8_t i_sps_id = hevc_get_pps_sps_id(*pp_pps);
595         *pp_sps = p_sys->hh.hevc.sps_list[i_sps_id].hevc_sps;
596         if (*pp_sps == NULL)
597             *pp_vps = NULL;
598         else
599         {
600             uint8_t i_vps_id = hevc_get_sps_vps_id(*pp_sps);
601             *pp_vps = p_sys->hh.hevc.vps_list[i_vps_id].hevc_vps;
602         }
603     }
606 struct hevc_sei_callback_s
608     hevc_sei_pic_timing_t *p_timing;
609     const hevc_sequence_parameter_set_t *p_sps;
612 static bool ParseHEVCSEI(const hxxx_sei_data_t *p_sei_data, void *priv)
614     if(p_sei_data->i_type == HXXX_SEI_PIC_TIMING)
615     {
616         struct hevc_sei_callback_s *s = priv;
617         if(likely(s->p_sps))
618             s->p_timing = hevc_decode_sei_pic_timing(p_sei_data->p_bs, s->p_sps);
619         return false;
620     }
621     return true;
624 static bool FillReorderInfoHEVC(decoder_t *p_dec, const block_t *p_block,
625                                 frame_info_t *p_info)
627     decoder_sys_t *p_sys = p_dec->p_sys;
628     hxxx_iterator_ctx_t itctx;
629     hxxx_iterator_init(&itctx, p_block->p_buffer, p_block->i_buffer,
630                        p_sys->hh.i_nal_length_size);
632     const uint8_t *p_nal; size_t i_nal;
633     struct
634     {
635         const uint8_t *p_nal;
636         size_t i_nal;
637     } sei_array[VT_MAX_SEI_COUNT];
638     size_t i_sei_count = 0;
640     while(hxxx_iterate_next(&itctx, &p_nal, &i_nal))
641     {
642         if(i_nal < 2 || hevc_getNALLayer(p_nal) > 0)
643             continue;
645         const enum hevc_nal_unit_type_e i_nal_type = hevc_getNALType(p_nal);
646         if (i_nal_type <= HEVC_NAL_IRAP_VCL23)
647         {
648             hevc_slice_segment_header_t *p_sli =
649                     hevc_decode_slice_header(p_nal, i_nal, true, GetxPSHEVC, p_sys);
650             if(!p_sli)
651                 return false;
653             /* XXX: Work-around a VT bug on recent devices (iPhone X, MacBook
654              * Pro 2017). The VT session will report a BadDataErr if you send a
655              * RASL frame just after a CRA one. Indeed, RASL frames are
656              * corrupted if the decoding start at an IRAP frame (IDR/CRA), VT
657              * is likely failing to handle this case. */
658             if (!p_sys->b_vt_feed && (i_nal_type != HEVC_NAL_IDR_W_RADL &&
659                                       i_nal_type != HEVC_NAL_IDR_N_LP))
660                 p_sys->b_drop_blocks = true;
661             else if (p_sys->b_drop_blocks)
662             {
663                 if (i_nal_type == HEVC_NAL_RASL_N || i_nal_type == HEVC_NAL_RASL_R)
664                 {
665                     hevc_rbsp_release_slice_header(p_sli);
666                     return false;
667                 }
668                 else
669                     p_sys->b_drop_blocks = false;
670             }
672             p_info->b_keyframe = i_nal_type >= HEVC_NAL_BLA_W_LP;
674             hevc_sequence_parameter_set_t *p_sps;
675             hevc_picture_parameter_set_t *p_pps;
676             hevc_video_parameter_set_t *p_vps;
677             GetxPSHEVC(hevc_get_slice_pps_id(p_sli), p_sys, &p_pps, &p_sps, &p_vps);
678             if(p_sps)
679             {
680                 struct hevc_sei_callback_s sei;
681                 sei.p_sps = p_sps;
682                 sei.p_timing = NULL;
684                 const int POC = hevc_compute_picture_order_count(p_sps, p_sli,
685                                                                  &p_sys->hevc_pocctx);
687                 for(size_t i=0; i<i_sei_count; i++)
688                     HxxxParseSEI(sei_array[i].p_nal, sei_array[i].i_nal,
689                                  2, ParseHEVCSEI, &sei);
691                 p_info->i_poc = POC;
692                 p_info->i_foc = POC; /* clearly looks wrong :/ */
693                 p_info->i_num_ts = hevc_get_num_clock_ts(p_sps, sei.p_timing);
694                 p_info->b_flush = (POC == 0);
695                 p_info->b_field = (p_info->i_num_ts == 1);
696                 p_info->b_progressive = hevc_frame_is_progressive(p_sps, sei.p_timing);
698                 /* Set frame rate for timings in case of missing rate */
699                 if( (!p_dec->fmt_in.video.i_frame_rate_base ||
700                      !p_dec->fmt_in.video.i_frame_rate) )
701                 {
702                     unsigned num, den;
703                     if(hevc_get_frame_rate(p_sps, p_vps, &num, &den))
704                         date_Change(&p_sys->pts, num, den);
705                 }
707                 if(sei.p_timing)
708                     hevc_release_sei_pic_timing(sei.p_timing);
710                 if(!p_sys->b_invalid_pic_reorder_max && p_vps)
711                 {
712                     vlc_mutex_lock(&p_sys->lock);
713                     p_sys->i_pic_reorder_max = hevc_get_max_num_reorder(p_vps);
714                     pic_holder_update_reorder_max(p_sys->pic_holder,
715                                                   p_sys->i_pic_reorder_max,
716                                                   p_info->i_num_ts);
717                     vlc_mutex_unlock(&p_sys->lock);
718                 }
720             }
722             hevc_rbsp_release_slice_header(p_sli);
723             return true; /* No need to parse further NAL */
724         }
725         else if(i_nal_type == HEVC_NAL_PREF_SEI)
726         {
727             if(i_sei_count < VT_MAX_SEI_COUNT)
728             {
729                 sei_array[i_sei_count].p_nal = p_nal;
730                 sei_array[i_sei_count++].i_nal = i_nal;
731             }
732         }
733     }
735     return false;
738 static CFMutableDictionaryRef GetDecoderExtradataHEVC(decoder_t *p_dec)
740     decoder_sys_t *p_sys = p_dec->p_sys;
742     CFMutableDictionaryRef extradata = nil;
743     if (p_dec->fmt_in.i_extra && p_sys->hh.b_is_xvcC)
744     {
745         /* copy DecoderConfiguration */
746         extradata = ExtradataInfoCreate(CFSTR("hvcC"),
747                                         p_dec->fmt_in.p_extra,
748                                         p_dec->fmt_in.i_extra);
749     }
750     else if (p_sys->hh.hevc.i_pps_count &&
751              p_sys->hh.hevc.i_sps_count &&
752              p_sys->hh.hevc.i_vps_count)
753     {
754         /* build DecoderConfiguration from gathered */
755         block_t *p_hvcC = hevc_helper_get_hvcc_config(&p_sys->hh);
756         if (p_hvcC)
757         {
758             extradata = ExtradataInfoCreate(CFSTR("hvcC"),
759                                             p_hvcC->p_buffer,
760                                             p_hvcC->i_buffer);
761             block_Release(p_hvcC);
762         }
763     }
764     return extradata;
767 static bool LateStartHEVC(decoder_t *p_dec)
769     decoder_sys_t *p_sys = p_dec->p_sys;
770     return (p_dec->fmt_in.i_extra == 0 &&
771             (!p_sys->hh.hevc.i_pps_count ||
772              !p_sys->hh.hevc.i_sps_count ||
773              !p_sys->hh.hevc.i_vps_count) );
776 static bool CodecSupportedHEVC(decoder_t *p_dec)
778     HXXXGetBestChroma(p_dec);
780     return true;
783 #define ConfigureVoutHEVC ConfigureVoutH264
784 #define ProcessBlockHEVC ProcessBlockH264
785 #define VideoToolboxNeedsToRestartHEVC VideoToolboxNeedsToRestartH264
787 static CFMutableDictionaryRef GetDecoderExtradataMPEG4(decoder_t *p_dec)
789     if (p_dec->fmt_in.i_extra)
790         return ESDSExtradataInfoCreate(p_dec, p_dec->fmt_in.p_extra,
791                                               p_dec->fmt_in.i_extra);
792     else
793         return nil; /* MPEG4 without esds ? */
796 static CFMutableDictionaryRef GetDecoderExtradataDefault(decoder_t *p_dec)
798     return ExtradataInfoCreate(NULL, NULL, 0); /* Empty Needed ? */
801 /* !Codec Specific */
803 static void InsertIntoDPB(decoder_sys_t *p_sys, frame_info_t *p_info)
805     frame_info_t **pp_lead_in = &p_sys->p_pic_reorder;
807     for( ;; pp_lead_in = & ((*pp_lead_in)->p_next))
808     {
809         bool b_insert;
810         if(*pp_lead_in == NULL)
811             b_insert = true;
812         else if(p_sys->b_poc_based_reorder)
813             b_insert = ((*pp_lead_in)->i_foc > p_info->i_foc);
814         else
815             b_insert = ((*pp_lead_in)->p_picture->date >= p_info->p_picture->date);
817         if(b_insert)
818         {
819             p_info->p_next = *pp_lead_in;
820             *pp_lead_in = p_info;
821             p_sys->i_pic_reorder += (p_info->b_field) ? 1 : 2;
822             break;
823         }
824     }
825 #if 0
826     for(frame_info_t *p_in=p_sys->p_pic_reorder; p_in; p_in = p_in->p_next)
827         printf(" %d", p_in->i_foc);
828     printf("\n");
829 #endif
832 static picture_t * RemoveOneFrameFromDPB(decoder_sys_t *p_sys)
834     frame_info_t *p_info = p_sys->p_pic_reorder;
835     if(p_info == NULL)
836         return NULL;
838     const int i_framepoc = p_info->i_poc;
840     picture_t *p_ret = NULL;
841     picture_t **pp_ret_last = &p_ret;
842     bool b_dequeue;
844     do
845     {
846         picture_t *p_field = p_info->p_picture;
848         /* Compute time if missing */
849         if (p_field->date == VLC_TS_INVALID)
850             p_field->date = date_Get(&p_sys->pts);
851         else
852             date_Set(&p_sys->pts, p_field->date);
854         /* Set next picture time, in case it is missing */
855         if (p_info->i_length)
856             date_Set(&p_sys->pts, p_field->date + p_info->i_length);
857         else
858             date_Increment(&p_sys->pts, p_info->i_num_ts);
860         *pp_ret_last = p_field;
861         pp_ret_last = &p_field->p_next;
863         p_sys->i_pic_reorder -= (p_info->b_field) ? 1 : 2;
865         p_sys->p_pic_reorder = p_info->p_next;
866         free(p_info);
867         p_info = p_sys->p_pic_reorder;
869         if (p_info)
870         {
871             if (p_sys->b_poc_based_reorder)
872                 b_dequeue = (p_info->i_poc == i_framepoc);
873             else
874                 b_dequeue = (p_field->date == p_info->p_picture->date);
875         }
876         else b_dequeue = false;
878     } while(b_dequeue);
880     return p_ret;
883 static void DrainDPBLocked(decoder_t *p_dec, bool flush)
885     decoder_sys_t *p_sys = p_dec->p_sys;
886     for( ;; )
887     {
888         picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
889         if (p_fields == NULL)
890             break;
891         do
892         {
893             picture_t *p_next = p_fields->p_next;
894             p_fields->p_next = NULL;
895             if (flush)
896                 picture_Release(p_fields);
897             else
898                 decoder_QueueVideo(p_dec, p_fields);
899             p_fields = p_next;
900         } while(p_fields != NULL);
901     }
904 static frame_info_t * CreateReorderInfo(decoder_t *p_dec, const block_t *p_block)
906     decoder_sys_t *p_sys = p_dec->p_sys;
907     frame_info_t *p_info = calloc(1, sizeof(*p_info));
908     if (!p_info)
909         return NULL;
911     if (p_sys->pf_fill_reorder_info)
912     {
913         if(!p_sys->pf_fill_reorder_info(p_dec, p_block, p_info))
914         {
915             free(p_info);
916             return NULL;
917         }
918     }
919     else
920     {
921         p_info->i_num_ts = 2;
922         p_info->b_progressive = true;
923         p_info->b_field = false;
924         p_info->b_keyframe = true;
925     }
927     p_info->i_length = p_block->i_length;
929     /* required for still pictures/menus */
930     p_info->b_forced = (p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE);
932     if (date_Get(&p_sys->pts) == VLC_TS_INVALID)
933         date_Set(&p_sys->pts, p_block->i_dts);
935     return p_info;
938 static void OnDecodedFrame(decoder_t *p_dec, frame_info_t *p_info)
940     decoder_sys_t *p_sys = p_dec->p_sys;
941     assert(p_info->p_picture);
942     while(p_info->b_flush || p_sys->i_pic_reorder >= (p_sys->i_pic_reorder_max * 2))
943     {
944         /* First check if DPB sizing was correct before removing one frame */
945         if (p_sys->p_pic_reorder && !p_info->b_flush &&
946             p_sys->i_pic_reorder_max < H264_MAX_DPB)
947         {
948             if(p_sys->b_poc_based_reorder && p_sys->p_pic_reorder->i_foc > p_info->i_foc)
949             {
950                 p_sys->b_invalid_pic_reorder_max = true;
951                 p_sys->i_pic_reorder_max++;
952                 pic_holder_update_reorder_max(p_sys->pic_holder,
953                                               p_sys->i_pic_reorder_max, p_info->i_num_ts);
954                 msg_Info(p_dec, "Raising max DPB to %"PRIu8, p_sys->i_pic_reorder_max);
955                 break;
956             }
957             else if (!p_sys->b_poc_based_reorder &&
958                      p_info->p_picture->date > VLC_TS_INVALID &&
959                      p_sys->p_pic_reorder->p_picture->date > p_info->p_picture->date)
960             {
961                 p_sys->b_invalid_pic_reorder_max = true;
962                 p_sys->i_pic_reorder_max++;
963                 pic_holder_update_reorder_max(p_sys->pic_holder,
964                                               p_sys->i_pic_reorder_max, p_info->i_num_ts);
965                 msg_Info(p_dec, "Raising max DPB to %"PRIu8, p_sys->i_pic_reorder_max);
966                 break;
967             }
968         }
970         picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
971         if (p_fields == NULL)
972             break;
973         do
974         {
975             picture_t *p_next = p_fields->p_next;
976             p_fields->p_next = NULL;
977             decoder_QueueVideo(p_dec, p_fields);
978             p_fields = p_next;
979         } while(p_fields != NULL);
980     }
982     InsertIntoDPB(p_sys, p_info);
985 static CMVideoCodecType CodecPrecheck(decoder_t *p_dec)
987     decoder_sys_t *p_sys = p_dec->p_sys;
989     /* check for the codec we can and want to decode */
990     switch (p_dec->fmt_in.i_codec) {
991         case VLC_CODEC_H264:
992             return kCMVideoCodecType_H264;
994         case VLC_CODEC_HEVC:
995             if (!deviceSupportsHEVC())
996             {
997                 msg_Warn(p_dec, "device doesn't support HEVC");
998                 return -1;
999             }
1000             return kCMVideoCodecType_HEVC;
1002         case VLC_CODEC_MP4V:
1003         {
1004             if (p_dec->fmt_in.i_original_fourcc == VLC_FOURCC( 'X','V','I','D' )) {
1005                 msg_Warn(p_dec, "XVID decoding not implemented, fallback on software");
1006                 return -1;
1007             }
1009             msg_Dbg(p_dec, "Will decode MP4V with original FourCC '%4.4s'", (char *)&p_dec->fmt_in.i_original_fourcc);
1010             return kCMVideoCodecType_MPEG4Video;
1011         }
1012 #if !TARGET_OS_IPHONE
1013         case VLC_CODEC_H263:
1014             return kCMVideoCodecType_H263;
1016             /* there are no DV or ProRes decoders on iOS, so bailout early */
1017         case VLC_CODEC_PRORES:
1018             /* the VT decoder can't differenciate between the ProRes flavors, so we do it */
1019             switch (p_dec->fmt_in.i_original_fourcc) {
1020                 case VLC_FOURCC( 'a','p','4','c' ):
1021                 case VLC_FOURCC( 'a','p','4','h' ):
1022                     return kCMVideoCodecType_AppleProRes4444;
1024                 case VLC_FOURCC( 'a','p','c','h' ):
1025                     return kCMVideoCodecType_AppleProRes422HQ;
1027                 case VLC_FOURCC( 'a','p','c','s' ):
1028                     return kCMVideoCodecType_AppleProRes422LT;
1030                 case VLC_FOURCC( 'a','p','c','o' ):
1031                     return kCMVideoCodecType_AppleProRes422Proxy;
1033                 default:
1034                     return kCMVideoCodecType_AppleProRes422;
1035             }
1037         case VLC_CODEC_DV:
1038             /* the VT decoder can't differenciate between PAL and NTSC, so we need to do it */
1039             switch (p_dec->fmt_in.i_original_fourcc) {
1040                 case VLC_FOURCC( 'd', 'v', 'c', ' '):
1041                 case VLC_FOURCC( 'd', 'v', ' ', ' '):
1042                     msg_Dbg(p_dec, "Decoding DV NTSC");
1043                     return kCMVideoCodecType_DVCNTSC;
1045                 case VLC_FOURCC( 'd', 'v', 's', 'd'):
1046                 case VLC_FOURCC( 'd', 'v', 'c', 'p'):
1047                 case VLC_FOURCC( 'D', 'V', 'S', 'D'):
1048                     msg_Dbg(p_dec, "Decoding DV PAL");
1049                     return kCMVideoCodecType_DVCPAL;
1050                 default:
1051                     return -1;
1052             }
1053 #endif
1054             /* mpgv / mp2v needs fixing, so disable it for now */
1055 #if 0
1056         case VLC_CODEC_MPGV:
1057             return kCMVideoCodecType_MPEG1Video;
1058         case VLC_CODEC_MP2V:
1059             return kCMVideoCodecType_MPEG2Video;
1060 #endif
1062         default:
1063 #ifndef NDEBUG
1064             msg_Err(p_dec, "'%4.4s' is not supported", (char *)&p_dec->fmt_in.i_codec);
1065 #endif
1066             return -1;
1067     }
1069     vlc_assert_unreachable();
1072 static CFMutableDictionaryRef CreateSessionDescriptionFormat(decoder_t *p_dec,
1073                                                              unsigned i_sar_num,
1074                                                              unsigned i_sar_den)
1076     decoder_sys_t *p_sys = p_dec->p_sys;
1078     CFMutableDictionaryRef decoderConfiguration = cfdict_create(0);
1079     if (decoderConfiguration == NULL)
1080         return nil;
1082     CFMutableDictionaryRef extradata = p_sys->pf_get_extradata
1083                                      ? p_sys->pf_get_extradata(p_dec) : nil;
1084     if(extradata)
1085     {
1086         /* then decoder will also fail if required, no need to handle it */
1087         CFDictionarySetValue(decoderConfiguration,
1088                              kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms,
1089                              extradata);
1090         CFRelease(extradata);
1091     }
1093     CFDictionarySetValue(decoderConfiguration,
1094                          kCVImageBufferChromaLocationBottomFieldKey,
1095                          kCVImageBufferChromaLocation_Left);
1096     CFDictionarySetValue(decoderConfiguration,
1097                          kCVImageBufferChromaLocationTopFieldKey,
1098                          kCVImageBufferChromaLocation_Left);
1100     /* pixel aspect ratio */
1101     if(i_sar_num && i_sar_den)
1102     {
1103         CFMutableDictionaryRef pixelaspectratio = cfdict_create(2);
1104         if(pixelaspectratio == NULL)
1105         {
1106             CFRelease(decoderConfiguration);
1107             return nil;
1108         }
1110         cfdict_set_int32(pixelaspectratio,
1111                          kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
1112                          i_sar_num);
1113         cfdict_set_int32(pixelaspectratio,
1114                          kCVImageBufferPixelAspectRatioVerticalSpacingKey,
1115                          i_sar_den);
1116         CFDictionarySetValue(decoderConfiguration,
1117                              kCVImageBufferPixelAspectRatioKey,
1118                              pixelaspectratio);
1119         CFRelease(pixelaspectratio);
1120     }
1122     /* Setup YUV->RGB matrix since VT can output BGRA directly. Don't setup
1123      * transfer and primaries since the transformation is done via the GL
1124      * fragment shader. */
1125     CFStringRef yuvmatrix;
1126     switch (p_dec->fmt_out.video.space)
1127     {
1128         case COLOR_SPACE_BT601:
1129             yuvmatrix = kCVImageBufferYCbCrMatrix_ITU_R_601_4;
1130             break;
1131         case COLOR_SPACE_BT2020:
1132 #pragma clang diagnostic push
1133 #pragma clang diagnostic ignored "-Wpartial-availability"
1134             if (&kCVImageBufferColorPrimaries_ITU_R_2020 != nil)
1135             {
1136                 yuvmatrix = kCVImageBufferColorPrimaries_ITU_R_2020;
1137                 break;
1138             }
1139 #pragma clang diagnostic pop
1140             /* fall through */
1141         case COLOR_SPACE_BT709:
1142         default:
1143             yuvmatrix = kCVImageBufferColorPrimaries_ITU_R_709_2;
1144             break;
1145     }
1146     CFDictionarySetValue(decoderConfiguration, kCVImageBufferYCbCrMatrixKey,
1147                          yuvmatrix);
1149 #pragma clang diagnostic push
1150 #pragma clang diagnostic ignored "-Wpartial-availability"
1152     /* enable HW accelerated playback, since this is optional on OS X
1153      * note that the backend may still fallback on software mode if no
1154      * suitable hardware is available */
1155     CFDictionarySetValue(decoderConfiguration,
1156                          kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
1157                          kCFBooleanTrue);
1159     /* on OS X, we can force VT to fail if no suitable HW decoder is available,
1160      * preventing the aforementioned SW fallback */
1161     if (var_InheritBool(p_dec, "videotoolbox-hw-decoder-only"))
1162         CFDictionarySetValue(decoderConfiguration,
1163                              kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder,
1164                              kCFBooleanTrue);
1166 #pragma clang diagnostic pop
1168     CFDictionarySetValue(decoderConfiguration,
1169                          kVTDecompressionPropertyKey_FieldMode,
1170                          kVTDecompressionProperty_FieldMode_DeinterlaceFields);
1171     CFDictionarySetValue(decoderConfiguration,
1172                          kVTDecompressionPropertyKey_DeinterlaceMode,
1173                          kVTDecompressionProperty_DeinterlaceMode_Temporal);
1175     return decoderConfiguration;
1178 static void PtsInit(decoder_t *p_dec)
1180     decoder_sys_t *p_sys = p_dec->p_sys;
1182     if( p_dec->fmt_in.video.i_frame_rate_base && p_dec->fmt_in.video.i_frame_rate )
1183     {
1184         date_Init( &p_sys->pts, p_dec->fmt_in.video.i_frame_rate * 2,
1185                    p_dec->fmt_in.video.i_frame_rate_base );
1186     }
1187     else date_Init( &p_sys->pts, 2 * 30000, 1001 );
1190 static int StartVideoToolbox(decoder_t *p_dec)
1192     decoder_sys_t *p_sys = p_dec->p_sys;
1194     /* Late starts */
1195     if(p_sys->pf_late_start && p_sys->pf_late_start(p_dec))
1196     {
1197         assert(p_sys->session == NULL);
1198         return VLC_SUCCESS;
1199     }
1201     /* Fills fmt_out (from extradata if any) */
1202     if(ConfigureVout(p_dec) != VLC_SUCCESS)
1203         return VLC_EGENERIC;
1205     /* destination pixel buffer attributes */
1206     CFMutableDictionaryRef destinationPixelBufferAttributes = cfdict_create(0);
1207     if(destinationPixelBufferAttributes == nil)
1208         return VLC_EGENERIC;
1210     CFMutableDictionaryRef decoderConfiguration =
1211         CreateSessionDescriptionFormat(p_dec,
1212                                        p_dec->fmt_out.video.i_sar_num,
1213                                        p_dec->fmt_out.video.i_sar_den);
1214     if(decoderConfiguration == nil)
1215     {
1216         CFRelease(destinationPixelBufferAttributes);
1217         return VLC_EGENERIC;
1218     }
1220     /* create video format description */
1221     OSStatus status = CMVideoFormatDescriptionCreate(
1222                                             kCFAllocatorDefault,
1223                                             p_sys->codec,
1224                                             p_dec->fmt_out.video.i_visible_width,
1225                                             p_dec->fmt_out.video.i_visible_height,
1226                                             decoderConfiguration,
1227                                             &p_sys->videoFormatDescription);
1228     if (status)
1229     {
1230         CFRelease(destinationPixelBufferAttributes);
1231         CFRelease(decoderConfiguration);
1232         msg_Err(p_dec, "video format description creation failed (%i)", (int)status);
1233         return VLC_EGENERIC;
1234     }
1236 #if !TARGET_OS_IPHONE
1237     CFDictionarySetValue(destinationPixelBufferAttributes,
1238                          kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey,
1239                          kCFBooleanTrue);
1240 #else
1241     CFDictionarySetValue(destinationPixelBufferAttributes,
1242                          kCVPixelBufferOpenGLESCompatibilityKey,
1243                          kCFBooleanTrue);
1244 #endif
1246     cfdict_set_int32(destinationPixelBufferAttributes,
1247                      kCVPixelBufferWidthKey, p_dec->fmt_out.video.i_visible_width);
1248     cfdict_set_int32(destinationPixelBufferAttributes,
1249                      kCVPixelBufferHeightKey, p_dec->fmt_out.video.i_visible_height);
1251     if (p_sys->i_cvpx_format != 0)
1252     {
1253         int chroma = htonl(p_sys->i_cvpx_format);
1254         msg_Warn(p_dec, "forcing CVPX format: %4.4s", (const char *) &chroma);
1255         cfdict_set_int32(destinationPixelBufferAttributes,
1256                          kCVPixelBufferPixelFormatTypeKey,
1257                          p_sys->i_cvpx_format);
1258     }
1260     cfdict_set_int32(destinationPixelBufferAttributes,
1261                      kCVPixelBufferBytesPerRowAlignmentKey, 16);
1263     /* setup decoder callback record */
1264     VTDecompressionOutputCallbackRecord decoderCallbackRecord;
1265     decoderCallbackRecord.decompressionOutputCallback = DecoderCallback;
1266     decoderCallbackRecord.decompressionOutputRefCon = p_dec;
1268     /* create decompression session */
1269     status = VTDecompressionSessionCreate(kCFAllocatorDefault,
1270                                           p_sys->videoFormatDescription,
1271                                           decoderConfiguration,
1272                                           destinationPixelBufferAttributes,
1273                                           &decoderCallbackRecord, &p_sys->session);
1274     CFRelease(decoderConfiguration);
1275     CFRelease(destinationPixelBufferAttributes);
1277     if (HandleVTStatus(p_dec, status, NULL) != VLC_SUCCESS)
1278         return VLC_EGENERIC;
1280     PtsInit(p_dec);
1282     return VLC_SUCCESS;
1285 static void StopVideoToolbox(decoder_t *p_dec)
1287     decoder_sys_t *p_sys = p_dec->p_sys;
1289     if (p_sys->session != nil)
1290     {
1291         Drain(p_dec, true);
1293         VTDecompressionSessionInvalidate(p_sys->session);
1294         CFRelease(p_sys->session);
1295         p_sys->session = nil;
1297 #if TARGET_OS_IPHONE
1298         /* In case of 4K 10bits (BGRA), we can easily reach the device max
1299          * memory when flushing. Indeed, we'll create a new VT session that
1300          * will reallocate frames while previous frames are still used by the
1301          * vout (and not released). To work-around this issue, we force a vout
1302          * change. */
1303         if (p_dec->fmt_out.i_codec == VLC_CODEC_CVPX_BGRA
1304          && p_dec->fmt_out.video.i_width * p_dec->fmt_out.video.i_height >= 8000000)
1305         {
1306             const video_format_t orig = p_dec->fmt_out.video;
1307             p_dec->fmt_out.video.i_width = p_dec->fmt_out.video.i_height =
1308             p_dec->fmt_out.video.i_visible_width = p_dec->fmt_out.video.i_visible_height = 64;
1309             (void) decoder_UpdateVideoFormat(p_dec);
1310             p_dec->fmt_out.video = orig;
1311         }
1312 #endif
1314         p_sys->b_format_propagated = false;
1315         p_dec->fmt_out.i_codec = 0;
1316     }
1318     if (p_sys->videoFormatDescription != nil) {
1319         CFRelease(p_sys->videoFormatDescription);
1320         p_sys->videoFormatDescription = nil;
1321     }
1322     p_sys->b_vt_feed = false;
1323     p_sys->b_drop_blocks = false;
1326 #pragma mark - module open and close
1329 static int OpenDecoder(vlc_object_t *p_this)
1331     decoder_t *p_dec = (decoder_t *)p_this;
1333 #if TARGET_OS_IPHONE
1334     if (unlikely([[UIDevice currentDevice].systemVersion floatValue] < 8.0)) {
1335         msg_Warn(p_dec, "decoder skipped as OS is too old");
1336         return VLC_EGENERIC;
1337     }
1338 #endif
1340     /* Fail if this module already failed to decode this ES */
1341     if (var_Type(p_dec, "videotoolbox-failed") != 0)
1342         return VLC_EGENERIC;
1344     /* check quickly if we can digest the offered data */
1345     CMVideoCodecType codec;
1347     codec = CodecPrecheck(p_dec);
1348     if (codec == -1)
1349         return VLC_EGENERIC;
1351     /* now that we see a chance to decode anything, allocate the
1352      * internals and start the decoding session */
1353     decoder_sys_t *p_sys;
1354     p_sys = calloc(1, sizeof(*p_sys));
1355     if (!p_sys)
1356         return VLC_ENOMEM;
1357     p_dec->p_sys = p_sys;
1358     p_sys->session = nil;
1359     p_sys->codec = codec;
1360     p_sys->videoFormatDescription = nil;
1361     p_sys->i_pic_reorder_max = 4;
1362     p_sys->vtsession_status = VTSESSION_STATUS_OK;
1363     p_sys->b_cvpx_format_forced = false;
1365     char *cvpx_chroma = var_InheritString(p_dec, "videotoolbox-cvpx-chroma");
1366     if (cvpx_chroma != NULL)
1367     {
1368         if (strlen(cvpx_chroma) != 4)
1369         {
1370             msg_Err(p_dec, "invalid videotoolbox-cvpx-chroma option");
1371             free(cvpx_chroma);
1372             free(p_sys);
1373             return VLC_EGENERIC;
1374         }
1375         memcpy(&p_sys->i_cvpx_format, cvpx_chroma, 4);
1376         p_sys->i_cvpx_format = ntohl(p_sys->i_cvpx_format);
1377         p_sys->b_cvpx_format_forced = true;
1378         free(cvpx_chroma);
1379     }
1381     p_sys->pic_holder = malloc(sizeof(struct pic_holder));
1382     if (!p_sys->pic_holder)
1383     {
1384         free(p_sys);
1385         return VLC_ENOMEM;
1386     }
1388     vlc_mutex_init(&p_sys->pic_holder->lock);
1389     vlc_cond_init(&p_sys->pic_holder->wait);
1390     p_sys->pic_holder->nb_field_out = 0;
1391     p_sys->pic_holder->closed = false;
1392     p_sys->pic_holder->field_reorder_max = p_sys->i_pic_reorder_max * 2;
1394     vlc_mutex_init(&p_sys->lock);
1396     p_dec->pf_decode = DecodeBlock;
1397     p_dec->pf_flush  = RequestFlush;
1399     switch(codec)
1400     {
1401         case kCMVideoCodecType_H264:
1402             p_sys->pf_codec_init = InitH264;
1403             p_sys->pf_codec_clean = CleanH264;
1404             p_sys->pf_codec_supported = CodecSupportedH264;
1405             p_sys->pf_late_start = LateStartH264;
1406             p_sys->pf_process_block = ProcessBlockH264;
1407             p_sys->pf_need_restart = VideoToolboxNeedsToRestartH264;
1408             p_sys->pf_configure_vout = ConfigureVoutH264;
1409             p_sys->pf_get_extradata = GetDecoderExtradataH264;
1410             p_sys->pf_fill_reorder_info = FillReorderInfoH264;
1411             p_sys->b_poc_based_reorder = true;
1412             break;
1414         case kCMVideoCodecType_HEVC:
1415             p_sys->pf_codec_init = InitHEVC;
1416             p_sys->pf_codec_clean = CleanHEVC;
1417             p_sys->pf_codec_supported = CodecSupportedHEVC;
1418             p_sys->pf_late_start = LateStartHEVC;
1419             p_sys->pf_process_block = ProcessBlockHEVC;
1420             p_sys->pf_need_restart = VideoToolboxNeedsToRestartHEVC;
1421             p_sys->pf_configure_vout = ConfigureVoutHEVC;
1422             p_sys->pf_get_extradata = GetDecoderExtradataHEVC;
1423             p_sys->pf_fill_reorder_info = FillReorderInfoHEVC;
1424             p_sys->b_poc_based_reorder = true;
1425             break;
1427         case kCMVideoCodecType_MPEG4Video:
1428             p_sys->pf_get_extradata = GetDecoderExtradataMPEG4;
1429             break;
1431         default:
1432             p_sys->pf_get_extradata = GetDecoderExtradataDefault;
1433             break;
1434     }
1436     if (p_sys->pf_codec_init && !p_sys->pf_codec_init(p_dec))
1437     {
1438         CloseDecoder(p_this);
1439         return VLC_EGENERIC;
1440     }
1441     if (p_sys->pf_codec_supported && !p_sys->pf_codec_supported(p_dec))
1442     {
1443         CloseDecoder(p_this);
1444         return VLC_EGENERIC;
1445     }
1447     int i_ret = StartVideoToolbox(p_dec);
1448     if (i_ret == VLC_SUCCESS)
1449         msg_Info(p_dec, "Using Video Toolbox to decode '%4.4s'",
1450                         (char *)&p_dec->fmt_in.i_codec);
1451     else
1452         CloseDecoder(p_this);
1453     return i_ret;
1456 static void pic_holder_clean(struct pic_holder *pic_holder)
1458     vlc_mutex_destroy(&pic_holder->lock);
1459     vlc_cond_destroy(&pic_holder->wait);
1460     free(pic_holder);
1463 static void CloseDecoder(vlc_object_t *p_this)
1465     decoder_t *p_dec = (decoder_t *)p_this;
1466     decoder_sys_t *p_sys = p_dec->p_sys;
1468     StopVideoToolbox(p_dec);
1470     if(p_sys->pf_codec_clean)
1471         p_sys->pf_codec_clean(p_dec);
1473     vlc_mutex_destroy(&p_sys->lock);
1475     vlc_mutex_lock(&p_sys->pic_holder->lock);
1476     if (p_sys->pic_holder->nb_field_out == 0)
1477     {
1478         vlc_mutex_unlock(&p_sys->pic_holder->lock);
1479         pic_holder_clean(p_sys->pic_holder);
1480     }
1481     else
1482     {
1483         p_sys->pic_holder->closed = true;
1484         vlc_mutex_unlock(&p_sys->pic_holder->lock);
1485     }
1486     free(p_sys);
1489 #pragma mark - helpers
1491 static BOOL deviceSupportsHEVC()
1493 #pragma clang diagnostic push
1494 #pragma clang diagnostic ignored "-Wpartial-availability"
1496 #if (TARGET_OS_OSX && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300) || \
1497     (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || \
1498     (TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED >= 110000)
1499     if (VTIsHardwareDecodeSupported != nil)
1500         return VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
1501     else
1502 #endif
1503 #pragma clang diagnostic pop
1504         return NO;
1507 static BOOL deviceSupportsAdvancedProfiles()
1509 #if TARGET_OS_IPHONE
1510     size_t size;
1511     cpu_type_t type;
1513     size = sizeof(type);
1514     sysctlbyname("hw.cputype", &type, &size, NULL, 0);
1516     /* Support for H264 profile HIGH 10 was introduced with the first 64bit Apple ARM SoC, the A7 */
1517     if (type == CPU_TYPE_ARM64)
1518         return YES;
1520     return NO;
1521 #else
1522     /* Assume that the CPU support advanced profiles if it can handle HEVC */
1523     return deviceSupportsHEVC();
1524 #endif
1527 static BOOL deviceSupportsAdvancedLevels()
1529 #if TARGET_OS_IPHONE
1530     #ifdef __LP64__
1531         size_t size;
1532         int32_t cpufamily;
1533         size = sizeof(cpufamily);
1534         sysctlbyname("hw.cpufamily", &cpufamily, &size, NULL, 0);
1536         /* Proper 4K decoding requires a Twister SoC
1537          * Everything below will kill the decoder daemon */
1538         if (cpufamily == CPUFAMILY_ARM_CYCLONE || cpufamily == CPUFAMILY_ARM_TYPHOON) {
1539             return NO;
1540         }
1542         return YES;
1543     #else
1544         /* we need a 64bit SoC for advanced levels */
1545         return NO;
1546     #endif
1547 #else
1548     return YES;
1549 #endif
1552 static inline void bo_add_mp4_tag_descr(bo_t *p_bo, uint8_t tag, uint32_t size)
1554     bo_add_8(p_bo, tag);
1555     for (int i = 3; i>0; i--)
1556         bo_add_8(p_bo, (size>>(7*i)) | 0x80);
1557     bo_add_8(p_bo, size & 0x7F);
1560 static CFMutableDictionaryRef ESDSExtradataInfoCreate(decoder_t *p_dec,
1561                                                       uint8_t *p_buf,
1562                                                       uint32_t i_buf_size)
1564     decoder_sys_t *p_sys = p_dec->p_sys;
1566     int full_size = 3 + 5 +13 + 5 + i_buf_size + 3;
1567     int config_size = 13 + 5 + i_buf_size;
1568     int padding = 12;
1570     bo_t bo;
1571     bool status = bo_init(&bo, 1024);
1572     if (status != true)
1573         return nil;
1575     bo_add_8(&bo, 0);       // Version
1576     bo_add_24be(&bo, 0);    // Flags
1578     // elementary stream description tag
1579     bo_add_mp4_tag_descr(&bo, 0x03, full_size);
1580     bo_add_16be(&bo, 0);    // esid
1581     bo_add_8(&bo, 0);       // stream priority (0-3)
1583     // decoder configuration description tag
1584     bo_add_mp4_tag_descr(&bo, 0x04, config_size);
1585     bo_add_8(&bo, 32);      // object type identification (32 == MPEG4)
1586     bo_add_8(&bo, 0x11);    // stream type
1587     bo_add_24be(&bo, 0);    // buffer size
1588     bo_add_32be(&bo, 0);    // max bitrate
1589     bo_add_32be(&bo, 0);    // avg bitrate
1591     // decoder specific description tag
1592     bo_add_mp4_tag_descr(&bo, 0x05, i_buf_size);
1593     bo_add_mem(&bo, i_buf_size, p_buf);
1595     // sync layer configuration description tag
1596     bo_add_8(&bo, 0x06);    // tag
1597     bo_add_8(&bo, 0x01);    // length
1598     bo_add_8(&bo, 0x02);    // no SL
1600     CFMutableDictionaryRef extradataInfo =
1601         ExtradataInfoCreate(CFSTR("esds"), bo.b->p_buffer, bo.b->i_buffer);
1602     bo_deinit(&bo);
1603     return extradataInfo;
1606 static int ConfigureVout(decoder_t *p_dec)
1608     /* return our proper VLC internal state */
1609     p_dec->fmt_out.video = p_dec->fmt_in.video;
1610     p_dec->fmt_out.video.p_palette = NULL;
1611     p_dec->fmt_out.i_codec = 0;
1613     if(p_dec->p_sys->pf_configure_vout &&
1614        !p_dec->p_sys->pf_configure_vout(p_dec))
1615         return VLC_EGENERIC;
1617     if (!p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den)
1618     {
1619         p_dec->fmt_out.video.i_sar_num = 1;
1620         p_dec->fmt_out.video.i_sar_den = 1;
1621     }
1623     if (!p_dec->fmt_out.video.i_visible_width || !p_dec->fmt_out.video.i_visible_height)
1624     {
1625         p_dec->fmt_out.video.i_visible_width = p_dec->fmt_out.video.i_width;
1626         p_dec->fmt_out.video.i_visible_height = p_dec->fmt_out.video.i_height;
1627     }
1629     p_dec->fmt_out.video.i_width = ALIGN_16( p_dec->fmt_out.video.i_visible_width );
1630     p_dec->fmt_out.video.i_height = ALIGN_16( p_dec->fmt_out.video.i_visible_height );
1632     return VLC_SUCCESS;
1635 static CFMutableDictionaryRef ExtradataInfoCreate(CFStringRef name,
1636                                                   void *p_data, size_t i_data)
1638     CFMutableDictionaryRef extradataInfo = cfdict_create(1);
1639     if (extradataInfo == nil)
1640         return nil;
1642     if (p_data == NULL)
1643         return nil;
1645     CFDataRef extradata = CFDataCreate(kCFAllocatorDefault, p_data, i_data);
1646     if (extradata == nil)
1647     {
1648         CFRelease(extradataInfo);
1649         return nil;
1650     }
1651     CFDictionarySetValue(extradataInfo, name, extradata);
1652     CFRelease(extradata);
1653     return extradataInfo;
1656 static CMSampleBufferRef VTSampleBufferCreate(decoder_t *p_dec,
1657                                               CMFormatDescriptionRef fmt_desc,
1658                                               block_t *p_block)
1660     OSStatus status;
1661     CMBlockBufferRef  block_buf = NULL;
1662     CMSampleBufferRef sample_buf = NULL;
1663     CMTime pts;
1664     if(!p_dec->p_sys->b_poc_based_reorder && p_block->i_pts == VLC_TS_INVALID)
1665         pts = CMTimeMake(p_block->i_dts, CLOCK_FREQ);
1666     else
1667         pts = CMTimeMake(p_block->i_pts, CLOCK_FREQ);
1669     CMSampleTimingInfo timeInfoArray[1] = { {
1670         .duration = CMTimeMake(p_block->i_length, 1),
1671         .presentationTimeStamp = pts,
1672         .decodeTimeStamp = CMTimeMake(p_block->i_dts, CLOCK_FREQ),
1673     } };
1675     status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,// structureAllocator
1676                                                 p_block->p_buffer,  // memoryBlock
1677                                                 p_block->i_buffer,  // blockLength
1678                                                 kCFAllocatorNull,   // blockAllocator
1679                                                 NULL,               // customBlockSource
1680                                                 0,                  // offsetToData
1681                                                 p_block->i_buffer,  // dataLength
1682                                                 false,              // flags
1683                                                 &block_buf);
1685     if (!status) {
1686         status = CMSampleBufferCreate(kCFAllocatorDefault,  // allocator
1687                                       block_buf,            // dataBuffer
1688                                       TRUE,                 // dataReady
1689                                       0,                    // makeDataReadyCallback
1690                                       0,                    // makeDataReadyRefcon
1691                                       fmt_desc,             // formatDescription
1692                                       1,                    // numSamples
1693                                       1,                    // numSampleTimingEntries
1694                                       timeInfoArray,        // sampleTimingArray
1695                                       0,                    // numSampleSizeEntries
1696                                       NULL,                 // sampleSizeArray
1697                                       &sample_buf);
1698         if (status != noErr)
1699             msg_Warn(p_dec, "sample buffer creation failure %i", (int)status);
1700     } else
1701         msg_Warn(p_dec, "cm block buffer creation failure %i", (int)status);
1703     if (block_buf != nil)
1704         CFRelease(block_buf);
1705     block_buf = nil;
1707     return sample_buf;
1710 static int HandleVTStatus(decoder_t *p_dec, OSStatus status,
1711                           enum vtsession_status * p_vtsession_status)
1713     decoder_sys_t *p_sys = p_dec->p_sys;
1715 #define VTERRCASE(x) \
1716     case x: msg_Warn(p_dec, "vt session error: '" #x "'"); break;
1718     switch (status)
1719     {
1720         case noErr:
1721             return VLC_SUCCESS;
1723         VTERRCASE(kVTPropertyNotSupportedErr)
1724         VTERRCASE(kVTPropertyReadOnlyErr)
1725         VTERRCASE(kVTParameterErr)
1726         VTERRCASE(kVTInvalidSessionErr)
1727         VTERRCASE(kVTAllocationFailedErr)
1728         VTERRCASE(kVTPixelTransferNotSupportedErr)
1729         VTERRCASE(kVTCouldNotFindVideoDecoderErr)
1730         VTERRCASE(kVTCouldNotCreateInstanceErr)
1731         VTERRCASE(kVTCouldNotFindVideoEncoderErr)
1732         VTERRCASE(kVTVideoDecoderBadDataErr)
1733         VTERRCASE(kVTVideoDecoderUnsupportedDataFormatErr)
1734         VTERRCASE(kVTVideoDecoderMalfunctionErr)
1735         VTERRCASE(kVTVideoEncoderMalfunctionErr)
1736         VTERRCASE(kVTVideoDecoderNotAvailableNowErr)
1737         VTERRCASE(kVTImageRotationNotSupportedErr)
1738         VTERRCASE(kVTVideoEncoderNotAvailableNowErr)
1739         VTERRCASE(kVTFormatDescriptionChangeNotSupportedErr)
1740         VTERRCASE(kVTInsufficientSourceColorDataErr)
1741         VTERRCASE(kVTCouldNotCreateColorCorrectionDataErr)
1742         VTERRCASE(kVTColorSyncTransformConvertFailedErr)
1743         VTERRCASE(kVTVideoDecoderAuthorizationErr)
1744         VTERRCASE(kVTVideoEncoderAuthorizationErr)
1745         VTERRCASE(kVTColorCorrectionPixelTransferFailedErr)
1746         VTERRCASE(kVTMultiPassStorageIdentifierMismatchErr)
1747         VTERRCASE(kVTMultiPassStorageInvalidErr)
1748         VTERRCASE(kVTFrameSiloInvalidTimeStampErr)
1749         VTERRCASE(kVTFrameSiloInvalidTimeRangeErr)
1750         VTERRCASE(kVTCouldNotFindTemporalFilterErr)
1751         VTERRCASE(kVTPixelTransferNotPermittedErr)
1752         case -12219:
1753             msg_Warn(p_dec, "vt session error: "
1754                      "'kVTColorCorrectionImageRotationFailedErr'");
1755             break;
1756         default:
1757             msg_Warn(p_dec, "unknown vt session error (%i)", (int)status);
1758     }
1759 #undef VTERRCASE
1761     if (p_vtsession_status)
1762     {
1763         switch (status)
1764         {
1765             case kVTParameterErr:
1766             case kCVReturnInvalidArgument:
1767                 *p_vtsession_status = VTSESSION_STATUS_ABORT;
1768                 break;
1769             case -8960 /* codecErr */:
1770             case kVTVideoDecoderMalfunctionErr:
1771             case -8969 /* codecBadDataErr */:
1772             case kVTVideoDecoderBadDataErr:
1773             case kVTInvalidSessionErr:
1774                 *p_vtsession_status = VTSESSION_STATUS_RESTART;
1775                 break;
1776             default:
1777                 *p_vtsession_status = VTSESSION_STATUS_OK;
1778                 break;
1779         }
1780     }
1781     return VLC_EGENERIC;
1784 #pragma mark - actual decoding
1786 static void RequestFlush(decoder_t *p_dec)
1788     decoder_sys_t *p_sys = p_dec->p_sys;
1790     vlc_mutex_lock(&p_sys->lock);
1791     p_sys->b_vt_flush = true;
1792     vlc_mutex_unlock(&p_sys->lock);
1795 static void Drain(decoder_t *p_dec, bool flush)
1797     decoder_sys_t *p_sys = p_dec->p_sys;
1799     /* draining: return last pictures of the reordered queue */
1800     vlc_mutex_lock(&p_sys->lock);
1801     p_sys->b_vt_flush = true;
1802     DrainDPBLocked(p_dec, flush);
1803     vlc_mutex_unlock(&p_sys->lock);
1805     if (p_sys->session && p_sys->b_vt_feed)
1806         VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
1808     vlc_mutex_lock(&p_sys->lock);
1809     assert(RemoveOneFrameFromDPB(p_sys) == NULL);
1810     p_sys->b_vt_flush = false;
1811     p_sys->b_vt_feed = false;
1812     p_sys->b_drop_blocks = false;
1813     vlc_mutex_unlock(&p_sys->lock);
1816 static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
1818     decoder_sys_t *p_sys = p_dec->p_sys;
1820     if (p_sys->b_vt_flush)
1821     {
1822         Drain(p_dec, true);
1823         PtsInit(p_dec);
1824     }
1826     if (p_block == NULL)
1827     {
1828         Drain(p_dec, false);
1829         return VLCDEC_SUCCESS;
1830     }
1832     vlc_mutex_lock(&p_sys->lock);
1834     if (p_block->i_flags & BLOCK_FLAG_INTERLACED_MASK)
1835     {
1836 #if TARGET_OS_IPHONE
1837         msg_Warn(p_dec, "VT decoder doesn't handle deinterlacing on iOS, "
1838                  "aborting...");
1839         p_sys->vtsession_status = VTSESSION_STATUS_ABORT;
1840 #else
1841         if (!p_sys->b_cvpx_format_forced
1842          && p_sys->i_cvpx_format == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
1843         {
1844             /* In case of interlaced content, force VT to output I420 since our
1845              * SW deinterlacer handle this chroma natively. This avoids having
1846              * 2 extra conversions (CVPX->I420 then I420->CVPX). */
1848             p_sys->i_cvpx_format = kCVPixelFormatType_420YpCbCr8Planar;
1849             msg_Warn(p_dec, "Interlaced content: forcing VT to output I420");
1850             if (p_sys->session != nil && p_sys->vtsession_status == VTSESSION_STATUS_OK)
1851             {
1852                 msg_Warn(p_dec, "restarting vt session (color changed)");
1853                 vlc_mutex_unlock(&p_sys->lock);
1855                 /* Drain before stopping */
1856                 Drain(p_dec, false);
1857                 StopVideoToolbox(p_dec);
1859                 vlc_mutex_lock(&p_sys->lock);
1860             }
1861         }
1862 #endif
1863     }
1865     if (p_sys->vtsession_status == VTSESSION_STATUS_RESTART)
1866     {
1867         if (p_sys->i_restart_count <= VT_RESTART_MAX)
1868         {
1869             msg_Warn(p_dec, "restarting vt session (dec callback failed)");
1870             vlc_mutex_unlock(&p_sys->lock);
1872             /* Session will be started by Late Start code block */
1873             StopVideoToolbox(p_dec);
1874             if (p_dec->fmt_in.i_extra == 0)
1875             {
1876                 /* Clean old parameter sets since they may be corrupt */
1877                 hxxx_helper_clean(&p_sys->hh);
1878             }
1880             vlc_mutex_lock(&p_sys->lock);
1881             p_sys->vtsession_status = VTSESSION_STATUS_OK;
1882         }
1883         else
1884         {
1885             msg_Warn(p_dec, "too many vt failure...");
1886             p_sys->vtsession_status = VTSESSION_STATUS_ABORT;
1887         }
1888     }
1890     if (p_sys->vtsession_status == VTSESSION_STATUS_ABORT)
1891     {
1892         vlc_mutex_unlock(&p_sys->lock);
1894         msg_Err(p_dec, "decoder failure, Abort.");
1895         /* Add an empty variable so that videotoolbox won't be loaded again for
1896          * this ES */
1897         var_Create(p_dec, "videotoolbox-failed", VLC_VAR_VOID);
1898         return VLCDEC_RELOAD;
1899     }
1900     vlc_mutex_unlock(&p_sys->lock);
1902     if (unlikely(p_block->i_flags&(BLOCK_FLAG_CORRUPTED)))
1903     {
1904         if (p_sys->b_vt_feed)
1905         {
1906             Drain(p_dec, false);
1907             PtsInit(p_dec);
1908         }
1909         goto skip;
1910     }
1912     bool b_config_changed = false;
1913     if(p_sys->pf_process_block)
1914     {
1915         p_block = p_sys->pf_process_block(p_dec, p_block, &b_config_changed);
1916         if (!p_block)
1917             return VLCDEC_SUCCESS;
1918     }
1920     frame_info_t *p_info = CreateReorderInfo(p_dec, p_block);
1921     if(unlikely(!p_info))
1922         goto skip;
1924     if (!p_sys->session /* Late Start */||
1925         (b_config_changed && p_info->b_flush))
1926     {
1927         if (p_sys->session &&
1928             p_sys->pf_need_restart &&
1929             p_sys->pf_need_restart(p_dec,p_sys->session))
1930         {
1931             msg_Dbg(p_dec, "parameters sets changed: draining decoder");
1932             Drain(p_dec, false);
1933             msg_Dbg(p_dec, "parameters sets changed: restarting decoder");
1934             StopVideoToolbox(p_dec);
1935         }
1937         if(!p_sys->session)
1938         {
1939             if ((p_sys->pf_codec_supported && !p_sys->pf_codec_supported(p_dec))
1940               || StartVideoToolbox(p_dec) != VLC_SUCCESS)
1941             {
1942                 /* The current device doesn't handle the profile/level, abort */
1943                 vlc_mutex_lock(&p_sys->lock);
1944                 p_sys->vtsession_status = VTSESSION_STATUS_ABORT;
1945                 vlc_mutex_unlock(&p_sys->lock);
1946             }
1947         }
1949         if (!p_sys->session) /* Start Failed */
1950         {
1951             free(p_info);
1952             goto skip;
1953         }
1954     }
1956     if (!p_sys->b_vt_feed && !p_info->b_keyframe)
1957     {
1958         free(p_info);
1959         goto skip;
1960     }
1962     CMSampleBufferRef sampleBuffer =
1963         VTSampleBufferCreate(p_dec, p_sys->videoFormatDescription, p_block);
1964     if (unlikely(!sampleBuffer))
1965     {
1966         free(p_info);
1967         goto skip;
1968     }
1970     VTDecodeInfoFlags flagOut;
1971     VTDecodeFrameFlags decoderFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
1973     OSStatus status =
1974         VTDecompressionSessionDecodeFrame(p_sys->session, sampleBuffer,
1975                                           decoderFlags, p_info, &flagOut);
1977     enum vtsession_status vtsession_status;
1978     if (HandleVTStatus(p_dec, status, &vtsession_status) == VLC_SUCCESS)
1979     {
1980         p_sys->b_vt_feed = true;
1981         if( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE )
1982             Drain( p_dec, false );
1983     }
1984     else
1985     {
1986         vlc_mutex_lock(&p_sys->lock);
1987         if (vtsession_status == VTSESSION_STATUS_RESTART)
1988             p_sys->i_restart_count++;
1989         p_sys->vtsession_status = vtsession_status;
1990         /* In case of abort, the decoder module will be reloaded next time
1991          * since we already modified the input block */
1992         vlc_mutex_unlock(&p_sys->lock);
1993     }
1994     CFRelease(sampleBuffer);
1996 skip:
1997     block_Release(p_block);
1998     return VLCDEC_SUCCESS;
2001 static int UpdateVideoFormat(decoder_t *p_dec, CVPixelBufferRef imageBuffer)
2003     NSDictionary *attachmentDict =
2004         (__bridge NSDictionary *)CVBufferGetAttachments(imageBuffer, kCVAttachmentMode_ShouldPropagate);
2006     if (attachmentDict != nil && attachmentDict.count > 0
2007      && p_dec->fmt_out.video.chroma_location == CHROMA_LOCATION_UNDEF)
2008     {
2009         NSString *chromaLocation = attachmentDict[(NSString *)kCVImageBufferChromaLocationTopFieldKey];
2010         if (chromaLocation != nil) {
2011             if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Left] ||
2012                 [chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_DV420])
2013                 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_LEFT;
2014             else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Center])
2015                 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_CENTER;
2016             else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_TopLeft])
2017                 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_TOP_LEFT;
2018             else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Top])
2019                 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_TOP_CENTER;
2020         }
2021         if (p_dec->fmt_out.video.chroma_location == CHROMA_LOCATION_UNDEF)
2022         {
2023             chromaLocation = attachmentDict[(NSString *)kCVImageBufferChromaLocationBottomFieldKey];
2024             if (chromaLocation != nil) {
2025                 if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_BottomLeft])
2026                     p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_BOTTOM_LEFT;
2027                 else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Bottom])
2028                     p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_BOTTOM_CENTER;
2029             }
2030         }
2031     }
2033     uint32_t cvfmt = CVPixelBufferGetPixelFormatType(imageBuffer);
2034     msg_Info(p_dec, "vt cvpx chroma: %4.4s",
2035              (const char *)&(uint32_t) { htonl(cvfmt) });
2036     switch (cvfmt)
2037     {
2038         case kCVPixelFormatType_422YpCbCr8:
2039         case 'yuv2':
2040             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_UYVY;
2041             assert(CVPixelBufferIsPlanar(imageBuffer) == false);
2042             break;
2043         case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
2044         case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
2045             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_NV12;
2046             assert(CVPixelBufferIsPlanar(imageBuffer) == true);
2047             break;
2048         case 'xf20': /* kCVPixelFormatType_420YpCbCr10BiPlanarFullRange */
2049         case 'x420': /* kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange */
2050             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_P010;
2051             assert(CVPixelBufferIsPlanar(imageBuffer) == true);
2052             break;
2053         case kCVPixelFormatType_420YpCbCr8Planar:
2054             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_I420;
2055             assert(CVPixelBufferIsPlanar(imageBuffer) == true);
2056             break;
2057         case kCVPixelFormatType_32BGRA:
2058             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_BGRA;
2059             assert(CVPixelBufferIsPlanar(imageBuffer) == false);
2060             break;
2061         default:
2062             p_dec->p_sys->vtsession_status = VTSESSION_STATUS_ABORT;
2063             return -1;
2064     }
2065     p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
2066     if (decoder_UpdateVideoFormat(p_dec) != 0)
2067     {
2068         p_dec->p_sys->vtsession_status = VTSESSION_STATUS_ABORT;
2069         return -1;
2070     }
2071     return 0;
2074 static void
2075 pic_holder_on_cvpx_released(CVPixelBufferRef cvpx, void *data, unsigned nb_fields)
2077     struct pic_holder *pic_holder = data;
2079     vlc_mutex_lock(&pic_holder->lock);
2080     assert((int) pic_holder->nb_field_out - nb_fields >= 0);
2081     pic_holder->nb_field_out -= nb_fields;
2082     if (pic_holder->nb_field_out == 0 && pic_holder->closed)
2083     {
2084         vlc_mutex_unlock(&pic_holder->lock);
2085         pic_holder_clean(pic_holder);
2086     }
2087     else
2088     {
2089         vlc_cond_broadcast(&pic_holder->wait);
2090         vlc_mutex_unlock(&pic_holder->lock);
2091     }
2094 static void
2095 pic_holder_update_reorder_max(struct pic_holder *pic_holder, uint8_t pic_reorder_max,
2096                               uint8_t nb_field)
2098     vlc_mutex_lock(&pic_holder->lock);
2100     pic_holder->field_reorder_max = pic_reorder_max * (nb_field < 2 ? 2 : nb_field);
2101     vlc_cond_signal(&pic_holder->wait);
2103     vlc_mutex_unlock(&pic_holder->lock);
2106 static int pic_holder_wait(struct pic_holder *pic_holder, const picture_t *pic)
2108     const uint8_t reserved_fields = 2 * (pic->i_nb_fields < 2 ? 2 : pic->i_nb_fields);
2110     vlc_mutex_lock(&pic_holder->lock);
2112     /* Wait 200 ms max. We can't really know what the video output will do with
2113      * output pictures (will they be rendered immediately ?), so don't wait
2114      * infinitely. The output will be paced anyway by the vlc_cond_timedwait()
2115      * call. */
2116     mtime_t deadline = mdate() + INT64_C(200000);
2117     int ret = 0;
2118     while (ret == 0 && pic_holder->field_reorder_max != 0
2119         && pic_holder->nb_field_out >= pic_holder->field_reorder_max + reserved_fields)
2120         ret = vlc_cond_timedwait(&pic_holder->wait, &pic_holder->lock, deadline);
2121     pic_holder->nb_field_out += pic->i_nb_fields;
2123     vlc_mutex_unlock(&pic_holder->lock);
2125     return ret;
2128 static void DecoderCallback(void *decompressionOutputRefCon,
2129                             void *sourceFrameRefCon,
2130                             OSStatus status,
2131                             VTDecodeInfoFlags infoFlags,
2132                             CVPixelBufferRef imageBuffer,
2133                             CMTime pts,
2134                             CMTime duration)
2136     VLC_UNUSED(duration);
2137     decoder_t *p_dec = (decoder_t *)decompressionOutputRefCon;
2138     decoder_sys_t *p_sys = p_dec->p_sys;
2139     frame_info_t *p_info = (frame_info_t *) sourceFrameRefCon;
2141     vlc_mutex_lock(&p_sys->lock);
2142     if (p_sys->b_vt_flush)
2143         goto end;
2145     enum vtsession_status vtsession_status;
2146     if (HandleVTStatus(p_dec, status, &vtsession_status) != VLC_SUCCESS)
2147     {
2148         if (p_sys->vtsession_status != VTSESSION_STATUS_ABORT)
2149         {
2150             p_sys->vtsession_status = vtsession_status;
2151             if (vtsession_status == VTSESSION_STATUS_RESTART)
2152                 p_sys->i_restart_count++;
2153         }
2154         goto end;
2155     }
2156     if (unlikely(!imageBuffer))
2157     {
2158         msg_Err(p_dec, "critical: null imageBuffer with a valid status");
2159         p_sys->vtsession_status = VTSESSION_STATUS_ABORT;
2160         goto end;
2161     }
2163     if (p_sys->vtsession_status == VTSESSION_STATUS_ABORT)
2164         goto end;
2166     if (unlikely(!p_sys->b_format_propagated)) {
2167         p_sys->b_format_propagated =
2168             UpdateVideoFormat(p_dec, imageBuffer) == VLC_SUCCESS;
2170         if (!p_sys->b_format_propagated)
2171             goto end;
2172         assert(p_dec->fmt_out.i_codec != 0);
2173     }
2175     if (infoFlags & kVTDecodeInfo_FrameDropped)
2176     {
2177         /* We can't trust VT, some decoded frames can be marked as dropped */
2178         msg_Dbg(p_dec, "decoder dropped frame");
2179     }
2181     if (!CMTIME_IS_VALID(pts))
2182         goto end;
2184     if (CVPixelBufferGetDataSize(imageBuffer) == 0)
2185         goto end;
2187     if(likely(p_info))
2188     {
2189         /* Unlock the mutex because decoder_NewPicture() is blocking. Indeed,
2190          * it can wait indefinitely when the input is paused. */
2192         vlc_mutex_unlock(&p_sys->lock);
2194         picture_t *p_pic = decoder_NewPicture(p_dec);
2195         if (!p_pic)
2196         {
2197             vlc_mutex_lock(&p_sys->lock);
2198             goto end;
2199         }
2201         p_info->p_picture = p_pic;
2203         p_pic->date = pts.value;
2204         p_pic->b_force = p_info->b_forced;
2205         p_pic->b_progressive = p_info->b_progressive;
2206         if(!p_pic->b_progressive)
2207         {
2208             p_pic->i_nb_fields = p_info->i_num_ts;
2209             p_pic->b_top_field_first = p_info->b_top_field_first;
2210         }
2212         if (cvpxpic_attach_with_cb(p_pic, imageBuffer, pic_holder_on_cvpx_released,
2213                                    p_sys->pic_holder) != VLC_SUCCESS)
2214         {
2215             vlc_mutex_lock(&p_sys->lock);
2216             goto end;
2217         }
2219         /* VT is not pacing frame allocation. If we are not fast enough to
2220          * render (release) the output pictures, the VT session can end up
2221          * allocating way too many frames. This can be problematic for 4K
2222          * 10bits. To fix this issue, we ensure that we don't have too many
2223          * output frames allocated by waiting for the vout to release them.
2224          *
2225          * FIXME: A proper way to fix this issue is to allow decoder modules to
2226          * specify the dpb and having the vout re-allocating output frames when
2227          * this number changes. */
2228         if (pic_holder_wait(p_sys->pic_holder, p_pic))
2229             msg_Warn(p_dec, "pic_holder_wait timed out");
2232         vlc_mutex_lock(&p_sys->lock);
2234         if (p_sys->b_vt_flush)
2235         {
2236             picture_Release(p_pic);
2237             goto end;
2238         }
2240         p_sys->i_restart_count = 0;
2242         OnDecodedFrame( p_dec, p_info );
2243         p_info = NULL;
2244     }
2246 end:
2247     free(p_info);
2248     vlc_mutex_unlock(&p_sys->lock);
2249     return;