configure: Improve detection of ibtool
[vlc.git] / modules / codec / videotoolbox.m
blob965262ebd9136996a72e55429cf92d3837edde79
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 "../packetizer/h264_nal.h"
36 #import "../packetizer/h264_slice.h"
37 #import "../packetizer/hxxx_nal.h"
38 #import "../packetizer/hxxx_sei.h"
39 #import "../video_chroma/copy.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 #if TARGET_OS_IPHONE
52 #import <UIKit/UIKit.h>
54 /* support iOS SDKs < v9.1 */
55 #ifndef CPUFAMILY_ARM_TWISTER
56 #define CPUFAMILY_ARM_TWISTER 0x92fb37c8
57 #endif
59 #endif
61 #pragma mark - module descriptor
63 static int OpenDecoder(vlc_object_t *);
64 static void CloseDecoder(vlc_object_t *);
66 #if MAC_OS_X_VERSION_MIN_ALLOWED < 1090
67 const CFStringRef kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder = CFSTR("EnableHardwareAcceleratedVideoDecoder");
68 const CFStringRef kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder = CFSTR("RequireHardwareAcceleratedVideoDecoder");
69 #endif
71 #define VT_REQUIRE_HW_DEC N_("Use Hardware decoders only")
72 #define VT_TEMPO_DEINTERLACE N_("Deinterlacing")
73 #define VT_TEMPO_DEINTERLACE_LONG N_("If interlaced content is detected, temporal deinterlacing is enabled at the expense of a pipeline delay.")
75 vlc_module_begin()
76 set_category(CAT_INPUT)
77 set_subcategory(SUBCAT_INPUT_VCODEC)
78 set_description(N_("VideoToolbox video decoder"))
79 set_capability("decoder",800)
80 set_callbacks(OpenDecoder, CloseDecoder)
82 add_bool("videotoolbox-temporal-deinterlacing", true, VT_TEMPO_DEINTERLACE, VT_TEMPO_DEINTERLACE_LONG, false)
83 add_bool("videotoolbox-hw-decoder-only", false, VT_REQUIRE_HW_DEC, VT_REQUIRE_HW_DEC, false)
84 vlc_module_end()
86 #pragma mark - local prototypes
88 static int ESDSCreate(decoder_t *, uint8_t *, uint32_t);
89 static int avcCFromAnnexBCreate(decoder_t *);
90 static int ExtradataInfoCreate(decoder_t *, CFStringRef, void *, size_t);
91 static int HandleVTStatus(decoder_t *, OSStatus);
92 static int DecodeBlock(decoder_t *, block_t *);
93 static void Flush(decoder_t *);
94 static void DecoderCallback(void *, void *, OSStatus, VTDecodeInfoFlags,
95                             CVPixelBufferRef, CMTime, CMTime);
96 void VTDictionarySetInt32(CFMutableDictionaryRef, CFStringRef, int);
97 static void copy420YpCbCr8Planar(picture_t *, CVPixelBufferRef buffer,
98                                  unsigned i_width, unsigned i_height);
99 static BOOL deviceSupportsAdvancedProfiles();
100 static BOOL deviceSupportsAdvancedLevels();
102 struct picture_sys_t {
103     CFTypeRef pixelBuffer;
106 typedef struct frame_info_t frame_info_t;
108 struct frame_info_t
110     picture_t *p_picture;
111     int i_poc;
112     int i_foc;
113     bool b_flush;
114     bool b_field;
115     bool b_progressive;
116     bool b_top_field_first;
117     uint8_t i_num_ts;
118     unsigned i_length;
119     frame_info_t *p_next;
122 #pragma mark - decoder structure
124 #define H264_MAX_DPB 16
126 struct decoder_sys_t
128     CMVideoCodecType            codec;
129     struct                      hxxx_helper hh;
131     bool                        b_vt_feed;
132     bool                        b_vt_flush;
133     VTDecompressionSessionRef   session;
134     CMVideoFormatDescriptionRef videoFormatDescription;
135     CFMutableDictionaryRef      decoderConfiguration;
136     CFMutableDictionaryRef      destinationPixelBufferAttributes;
137     CFMutableDictionaryRef      extradataInfo;
139     vlc_mutex_t                 lock;
140     frame_info_t               *p_pic_reorder;
141     uint8_t                     i_pic_reorder;
142     uint8_t                     i_pic_reorder_max;
143     bool                        b_invalid_pic_reorder_max;
144     bool                        b_poc_based_reorder;
145     bool                        b_enable_temporal_processing;
147     bool                        b_format_propagated;
148     bool                        b_abort;
150     poc_context_t               pocctx;
151     date_t                      pts;
154 #pragma mark - start & stop
156 static void GetSPSPPS(uint8_t i_pps_id, void *priv,
157                       const h264_sequence_parameter_set_t **pp_sps,
158                       const h264_picture_parameter_set_t **pp_pps)
160     decoder_sys_t *p_sys = priv;
162     *pp_pps = p_sys->hh.h264.pps_list[i_pps_id].h264_pps;
163     if(*pp_pps == NULL)
164         *pp_sps = NULL;
165     else
166         *pp_sps = p_sys->hh.h264.sps_list[(*pp_pps)->i_sps_id].h264_sps;
169 struct sei_callback_s
171     uint8_t i_pic_struct;
172     const h264_sequence_parameter_set_t *p_sps;
175 static bool ParseH264SEI(const hxxx_sei_data_t *p_sei_data, void *priv)
178     if(p_sei_data->i_type == HXXX_SEI_PIC_TIMING)
179     {
180         struct sei_callback_s *s = priv;
181         if(s->p_sps && s->p_sps->vui.b_valid)
182         {
183             if(s->p_sps->vui.b_hrd_parameters_present_flag)
184             {
185                 bs_read(p_sei_data->p_bs, s->p_sps->vui.i_cpb_removal_delay_length_minus1 + 1);
186                 bs_read(p_sei_data->p_bs, s->p_sps->vui.i_dpb_output_delay_length_minus1 + 1);
187             }
189             if(s->p_sps->vui.b_pic_struct_present_flag)
190                s->i_pic_struct = bs_read( p_sei_data->p_bs, 4);
191         }
192         return false;
193     }
195     return true;
198 static bool ParseH264NAL(decoder_t *p_dec,
199                          const uint8_t *p_buffer, size_t i_buffer,
200                          uint8_t i_nal_length_size, frame_info_t *p_info)
202     decoder_sys_t *p_sys = p_dec->p_sys;
203     hxxx_iterator_ctx_t itctx;
204     hxxx_iterator_init(&itctx, p_buffer, i_buffer, i_nal_length_size);
206     const uint8_t *p_nal; size_t i_nal;
207     const uint8_t *p_sei_nal = NULL; size_t i_sei_nal = 0;
208     while(hxxx_iterate_next(&itctx, &p_nal, &i_nal))
209     {
210         if(i_nal < 2)
211             continue;
213         const enum h264_nal_unit_type_e i_nal_type = p_nal[0] & 0x1F;
215         if (i_nal_type <= H264_NAL_SLICE_IDR && i_nal_type != H264_NAL_UNKNOWN)
216         {
217             h264_slice_t slice;
218             if(!h264_decode_slice(p_nal, i_nal, GetSPSPPS, p_sys, &slice))
219                 return false;
221             const h264_sequence_parameter_set_t *p_sps;
222             const h264_picture_parameter_set_t *p_pps;
223             GetSPSPPS(slice.i_pic_parameter_set_id, p_sys, &p_sps, &p_pps);
224             if(p_sps)
225             {
226                 if(!p_sys->b_invalid_pic_reorder_max && i_nal_type == H264_NAL_SLICE_IDR)
227                 {
228                     unsigned dummy;
229                     uint8_t i_reorder;
230                     h264_get_dpb_values(p_sps, &i_reorder, &dummy);
231                     vlc_mutex_lock(&p_sys->lock);
232                     p_sys->i_pic_reorder_max = i_reorder;
233                     vlc_mutex_unlock(&p_sys->lock);
234                 }
236                 int bFOC;
237                 h264_compute_poc(p_sps, &slice, &p_sys->pocctx,
238                                  &p_info->i_poc, &p_info->i_foc, &bFOC);
240                 p_info->b_flush = (slice.type == H264_SLICE_TYPE_I) || slice.has_mmco5;
241                 p_info->b_field = slice.i_field_pic_flag;
242                 p_info->b_progressive = !p_sps->mb_adaptive_frame_field_flag &&
243                                         !slice.i_field_pic_flag;
245                 struct sei_callback_s sei;
246                 sei.p_sps = p_sps;
247                 sei.i_pic_struct = UINT8_MAX;
249                 if(p_sei_nal)
250                     HxxxParseSEI(p_sei_nal, i_sei_nal, 1, ParseH264SEI, &sei);
252                 p_info->i_num_ts = h264_get_num_ts(p_sps, &slice, sei.i_pic_struct,
253                                                    p_info->i_foc, bFOC);
255                 if(!p_info->b_progressive)
256                     p_info->b_top_field_first = (sei.i_pic_struct % 2 == 1);
258                 /* Set frame rate for timings in case of missing rate */
259                 if( (!p_dec->fmt_in.video.i_frame_rate_base ||
260                      !p_dec->fmt_in.video.i_frame_rate) &&
261                     p_sps->vui.i_time_scale && p_sps->vui.i_num_units_in_tick )
262                 {
263                     date_Change( &p_sys->pts, p_sps->vui.i_time_scale,
264                                               p_sps->vui.i_num_units_in_tick );
265                 }
266             }
268             return true; /* No need to parse further NAL */
269         }
270         else if(i_nal_type == H264_NAL_SEI)
271         {
272             p_sei_nal = p_nal;
273             i_sei_nal = i_nal;
274         }
275     }
277     return false;
280 static void InsertIntoDPB(decoder_sys_t *p_sys, frame_info_t *p_info)
282     frame_info_t **pp_lead_in = &p_sys->p_pic_reorder;
284     for( ;; pp_lead_in = & ((*pp_lead_in)->p_next))
285     {
286         bool b_insert;
287         if(*pp_lead_in == NULL)
288             b_insert = true;
289         else if(p_sys->b_poc_based_reorder)
290             b_insert = ((*pp_lead_in)->i_foc > p_info->i_foc);
291         else
292             b_insert = ((*pp_lead_in)->p_picture->date >= p_info->p_picture->date);
294         if(b_insert)
295         {
296             p_info->p_next = *pp_lead_in;
297             *pp_lead_in = p_info;
298             p_sys->i_pic_reorder += (p_info->b_field) ? 1 : 2;
299             break;
300         }
301     }
302 #if 0
303     for(frame_info_t *p_in=p_sys->p_pic_reorder; p_in; p_in = p_in->p_next)
304         printf(" %d", p_in->i_foc);
305     printf("\n");
306 #endif
309 static picture_t * RemoveOneFrameFromDPB(decoder_sys_t *p_sys)
311     frame_info_t *p_info = p_sys->p_pic_reorder;
312     if(p_info == NULL)
313         return NULL;
315     const int i_framepoc = p_info->i_poc;
317     picture_t *p_ret = NULL;
318     picture_t **pp_ret_last = &p_ret;
319     bool b_dequeue;
321     do
322     {
323         picture_t *p_field = p_info->p_picture;
325         /* Compute time if missing */
326         if (p_field->date == VLC_TS_INVALID)
327             p_field->date = date_Get(&p_sys->pts);
328         else
329             date_Set(&p_sys->pts, p_field->date);
331         /* Set next picture time, in case it is missing */
332         if (p_info->i_length)
333             date_Set(&p_sys->pts, p_field->date + p_info->i_length);
334         else
335             date_Increment(&p_sys->pts, p_info->i_num_ts);
337         *pp_ret_last = p_field;
338         pp_ret_last = &p_field->p_next;
340         p_sys->i_pic_reorder -= (p_info->b_field) ? 1 : 2;
342         p_sys->p_pic_reorder = p_info->p_next;
343         free(p_info);
344         p_info = p_sys->p_pic_reorder;
346         if (p_info)
347         {
348             if (p_sys->b_poc_based_reorder)
349                 b_dequeue = (p_info->i_poc == i_framepoc);
350             else
351                 b_dequeue = (p_field->date == p_info->p_picture->date);
352         }
353         else b_dequeue = false;
355     } while(b_dequeue);
357     return p_ret;
360 static void DrainDPB(decoder_t *p_dec)
362     decoder_sys_t *p_sys = p_dec->p_sys;
363     for( ;; )
364     {
365         picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
366         if (p_fields == NULL)
367             break;
368         do
369         {
370             picture_t *p_next = p_fields->p_next;
371             p_fields->p_next = NULL;
372             decoder_QueueVideo(p_dec, p_fields);
373             p_fields = p_next;
374         } while(p_fields != NULL);
375     }
378 static frame_info_t * CreateReorderInfo(decoder_t *p_dec, const block_t *p_block)
380     decoder_sys_t *p_sys = p_dec->p_sys;
381     frame_info_t *p_info = calloc(1, sizeof(*p_info));
382     if (!p_info)
383         return NULL;
385     if (p_sys->b_poc_based_reorder)
386     {
387         if(p_sys->codec != kCMVideoCodecType_H264 ||
388            !ParseH264NAL(p_dec, p_block->p_buffer, p_block->i_buffer, 4, p_info))
389         {
390             assert(p_sys->codec == kCMVideoCodecType_H264);
391             free(p_info);
392             return NULL;
393         }
394     }
395     else
396     {
397         p_info->i_num_ts = 2;
398         p_info->b_progressive = true;
399         p_info->b_field = false;
400     }
402     p_info->i_length = p_block->i_length;
404     if (date_Get(&p_sys->pts) == VLC_TS_INVALID)
405         date_Set(&p_sys->pts, p_block->i_dts);
407     return p_info;
410 static void OnDecodedFrame(decoder_t *p_dec, frame_info_t *p_info)
412     decoder_sys_t *p_sys = p_dec->p_sys;
413     assert(p_info->p_picture);
414     while(p_info->b_flush || p_sys->i_pic_reorder >= (p_sys->i_pic_reorder_max * 2))
415     {
416         /* First check if DPB sizing was correct before removing one frame */
417         if (p_sys->p_pic_reorder && !p_info->b_flush &&
418             p_sys->i_pic_reorder_max < H264_MAX_DPB)
419         {
420             if(p_sys->b_poc_based_reorder && p_sys->p_pic_reorder->i_foc > p_info->i_foc)
421             {
422                 p_sys->b_invalid_pic_reorder_max = true;
423                 p_sys->i_pic_reorder_max++;
424                 msg_Info(p_dec, "Raising max DPB to %"PRIu8, p_sys->i_pic_reorder_max);
425                 break;
426             }
427             else if (!p_sys->b_poc_based_reorder &&
428                      p_info->p_picture->date > VLC_TS_INVALID &&
429                      p_sys->p_pic_reorder->p_picture->date > p_info->p_picture->date)
430             {
431                 p_sys->b_invalid_pic_reorder_max = true;
432                 p_sys->i_pic_reorder_max++;
433                 msg_Info(p_dec, "Raising max DPB to %"PRIu8, p_sys->i_pic_reorder_max);
434                 break;
435             }
436         }
438         picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
439         if (p_fields == NULL)
440             break;
441         do
442         {
443             picture_t *p_next = p_fields->p_next;
444             p_fields->p_next = NULL;
445             decoder_QueueVideo(p_dec, p_fields);
446             p_fields = p_next;
447         } while(p_fields != NULL);
448     }
450     InsertIntoDPB(p_sys, p_info);
453 static CMVideoCodecType CodecPrecheck(decoder_t *p_dec)
455     uint8_t i_profile = 0xFF, i_level = 0xFF;
456     bool b_ret = false;
457     CMVideoCodecType codec;
459     /* check for the codec we can and want to decode */
460     switch (p_dec->fmt_in.i_codec) {
461         case VLC_CODEC_H264:
462             codec = kCMVideoCodecType_H264;
464             b_ret = h264_get_profile_level(&p_dec->fmt_in, &i_profile, &i_level, NULL);
465             if (!b_ret) {
466                 msg_Warn(p_dec, "H264 profile and level parsing failed because it didn't arrive yet");
467                 return kCMVideoCodecType_H264;
468             }
470             msg_Dbg(p_dec, "trying to decode MPEG-4 Part 10: profile %" PRIx8 ", level %" PRIx8, i_profile, i_level);
472             switch (i_profile) {
473                 case PROFILE_H264_BASELINE:
474                 case PROFILE_H264_MAIN:
475                 case PROFILE_H264_HIGH:
476                     break;
478                 case PROFILE_H264_HIGH_10:
479                 {
480                     if (deviceSupportsAdvancedProfiles())
481                         break;
482                 }
484                 default:
485                 {
486                     msg_Dbg(p_dec, "unsupported H264 profile %" PRIx8, i_profile);
487                     return -1;
488                 }
489             }
491 #if !TARGET_OS_IPHONE
492             /* a level higher than 5.2 was not tested, so don't dare to
493              * try to decode it*/
494             if (i_level > 52) {
495                 msg_Dbg(p_dec, "unsupported H264 level %" PRIx8, i_level);
496                 return -1;
497             }
498 #else
499             /* on SoC A8, 4.2 is the highest specified profile */
500             if (i_level > 42) {
501                 /* on Twister, we can do up to 5.2 */
502                 if (!deviceSupportsAdvancedLevels() || i_level > 52) {
503                     msg_Dbg(p_dec, "unsupported H264 level %" PRIx8, i_level);
504                     return -1;
505                 }
506             }
507 #endif
509             break;
510         case VLC_CODEC_MP4V:
511         {
512             if (p_dec->fmt_in.i_original_fourcc == VLC_FOURCC( 'X','V','I','D' )) {
513                 msg_Warn(p_dec, "XVID decoding not implemented, fallback on software");
514                 return -1;
515             }
517             msg_Dbg(p_dec, "Will decode MP4V with original FourCC '%4.4s'", (char *)&p_dec->fmt_in.i_original_fourcc);
518             codec = kCMVideoCodecType_MPEG4Video;
520             break;
521         }
522 #if !TARGET_OS_IPHONE
523         case VLC_CODEC_H263:
524             codec = kCMVideoCodecType_H263;
525             break;
527             /* there are no DV or ProRes decoders on iOS, so bailout early */
528         case VLC_CODEC_PRORES:
529             /* the VT decoder can't differenciate between the ProRes flavors, so we do it */
530             switch (p_dec->fmt_in.i_original_fourcc) {
531                 case VLC_FOURCC( 'a','p','4','c' ):
532                 case VLC_FOURCC( 'a','p','4','h' ):
533                     codec = kCMVideoCodecType_AppleProRes4444;
534                     break;
536                 case VLC_FOURCC( 'a','p','c','h' ):
537                     codec = kCMVideoCodecType_AppleProRes422HQ;
538                     break;
540                 case VLC_FOURCC( 'a','p','c','s' ):
541                     codec = kCMVideoCodecType_AppleProRes422LT;
542                     break;
544                 case VLC_FOURCC( 'a','p','c','o' ):
545                     codec = kCMVideoCodecType_AppleProRes422Proxy;
546                     break;
548                 default:
549                     codec = kCMVideoCodecType_AppleProRes422;
550                     break;
551             }
552             if (codec != 0)
553                 break;
555         case VLC_CODEC_DV:
556             /* the VT decoder can't differenciate between PAL and NTSC, so we need to do it */
557             switch (p_dec->fmt_in.i_original_fourcc) {
558                 case VLC_FOURCC( 'd', 'v', 'c', ' '):
559                 case VLC_FOURCC( 'd', 'v', ' ', ' '):
560                     msg_Dbg(p_dec, "Decoding DV NTSC");
561                     codec = kCMVideoCodecType_DVCNTSC;
562                     break;
564                 case VLC_FOURCC( 'd', 'v', 's', 'd'):
565                 case VLC_FOURCC( 'd', 'v', 'c', 'p'):
566                 case VLC_FOURCC( 'D', 'V', 'S', 'D'):
567                     msg_Dbg(p_dec, "Decoding DV PAL");
568                     codec = kCMVideoCodecType_DVCPAL;
569                     break;
571                 default:
572                     break;
573             }
574             if (codec != 0)
575                 break;
576 #endif
577             /* mpgv / mp2v needs fixing, so disable it for now */
578 #if 0
579         case VLC_CODEC_MPGV:
580             codec = kCMVideoCodecType_MPEG1Video;
581             break;
582         case VLC_CODEC_MP2V:
583             codec = kCMVideoCodecType_MPEG2Video;
584             break;
585 #endif
587         default:
588 #ifndef NDEBUG
589             msg_Err(p_dec, "'%4.4s' is not supported", (char *)&p_dec->fmt_in.i_codec);
590 #endif
591             return -1;
592     }
594     return codec;
597 static int StartVideoToolbox(decoder_t *p_dec)
599     decoder_sys_t *p_sys = p_dec->p_sys;
600     OSStatus status;
602     assert(p_sys->extradataInfo != nil);
604     p_sys->decoderConfiguration =
605         CFDictionaryCreateMutable(kCFAllocatorDefault, 2,
606                                   &kCFTypeDictionaryKeyCallBacks,
607                                   &kCFTypeDictionaryValueCallBacks);
608     if (p_sys->decoderConfiguration == NULL)
609         return VLC_ENOMEM;
611     CFDictionarySetValue(p_sys->decoderConfiguration,
612                          kCVImageBufferChromaLocationBottomFieldKey,
613                          kCVImageBufferChromaLocation_Left);
614     CFDictionarySetValue(p_sys->decoderConfiguration,
615                          kCVImageBufferChromaLocationTopFieldKey,
616                          kCVImageBufferChromaLocation_Left);
618     CFDictionarySetValue(p_sys->decoderConfiguration,
619                          kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms,
620                          p_sys->extradataInfo);
622     /* pixel aspect ratio */
623     CFMutableDictionaryRef pixelaspectratio =
624         CFDictionaryCreateMutable(kCFAllocatorDefault, 2,
625                                   &kCFTypeDictionaryKeyCallBacks,
626                                   &kCFTypeDictionaryValueCallBacks);
628     const unsigned i_video_width = p_dec->fmt_out.video.i_width;
629     const unsigned i_video_height = p_dec->fmt_out.video.i_height;
630     const unsigned i_sar_num = p_dec->fmt_out.video.i_sar_num;
631     const unsigned i_sar_den = p_dec->fmt_out.video.i_sar_den;
633     if( p_dec->fmt_in.video.i_frame_rate_base && p_dec->fmt_in.video.i_frame_rate )
634     {
635         date_Init( &p_sys->pts, p_dec->fmt_in.video.i_frame_rate * 2,
636                                 p_dec->fmt_in.video.i_frame_rate_base );
637     }
638     else date_Init( &p_sys->pts, 2 * 30000, 1001 );
640     VTDictionarySetInt32(pixelaspectratio,
641                          kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
642                          i_sar_num);
643     VTDictionarySetInt32(pixelaspectratio,
644                          kCVImageBufferPixelAspectRatioVerticalSpacingKey,
645                          i_sar_den);
646     CFDictionarySetValue(p_sys->decoderConfiguration,
647                          kCVImageBufferPixelAspectRatioKey,
648                          pixelaspectratio);
649     CFRelease(pixelaspectratio);
651     /* enable HW accelerated playback, since this is optional on OS X
652      * note that the backend may still fallback on software mode if no
653      * suitable hardware is available */
654     CFDictionarySetValue(p_sys->decoderConfiguration,
655                          kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
656                          kCFBooleanTrue);
658     /* on OS X, we can force VT to fail if no suitable HW decoder is available,
659      * preventing the aforementioned SW fallback */
660     if (var_InheritInteger(p_dec, "videotoolbox-hw-decoder-only"))
661         CFDictionarySetValue(p_sys->decoderConfiguration,
662                              kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder,
663                              kCFBooleanTrue);
665     if (p_sys->b_enable_temporal_processing)
666     {
667         msg_Dbg(p_dec, "Interlaced content detected, inserting temporal deinterlacer");
668         CFDictionarySetValue(p_sys->decoderConfiguration,
669                              kVTDecompressionPropertyKey_FieldMode,
670                              kVTDecompressionProperty_FieldMode_DeinterlaceFields);
671         CFDictionarySetValue(p_sys->decoderConfiguration,
672                              kVTDecompressionPropertyKey_DeinterlaceMode,
673                              kVTDecompressionProperty_DeinterlaceMode_Temporal);
674     }
676     /* create video format description */
677     status = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
678                                             p_sys->codec,
679                                             i_video_width,
680                                             i_video_height,
681                                             p_sys->decoderConfiguration,
682                                             &p_sys->videoFormatDescription);
683     if (status) {
684         CFRelease(p_sys->decoderConfiguration);
685         msg_Err(p_dec, "video format description creation failed (%i)", (int)status);
686         return VLC_EGENERIC;
687     }
689     /* destination pixel buffer attributes */
690     p_sys->destinationPixelBufferAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault,
691                                                                         2,
692                                                                         &kCFTypeDictionaryKeyCallBacks,
693                                                                         &kCFTypeDictionaryValueCallBacks);
695 #if !TARGET_OS_IPHONE
696     CFDictionarySetValue(p_sys->destinationPixelBufferAttributes,
697                          kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey,
698                          kCFBooleanTrue);
699 #else
700     CFDictionarySetValue(p_sys->destinationPixelBufferAttributes,
701                          kCVPixelBufferOpenGLESCompatibilityKey,
702                          kCFBooleanTrue);
703 #endif
705     VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
706                          kCVPixelBufferWidthKey,
707                          i_video_width);
708     VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
709                          kCVPixelBufferHeightKey,
710                          i_video_height);
711     VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
712                          kCVPixelBufferBytesPerRowAlignmentKey,
713                          i_video_width * 2);
715     /* setup decoder callback record */
716     VTDecompressionOutputCallbackRecord decoderCallbackRecord;
717     decoderCallbackRecord.decompressionOutputCallback = DecoderCallback;
718     decoderCallbackRecord.decompressionOutputRefCon = p_dec;
720     /* create decompression session */
721     status = VTDecompressionSessionCreate(kCFAllocatorDefault,
722                                           p_sys->videoFormatDescription,
723                                           p_sys->decoderConfiguration,
724                                           p_sys->destinationPixelBufferAttributes,
725                                           &decoderCallbackRecord, &p_sys->session);
727     if (HandleVTStatus(p_dec, status) != VLC_SUCCESS)
728         return VLC_EGENERIC;
730     return VLC_SUCCESS;
733 static void StopVideoToolbox(decoder_t *p_dec, bool b_reset_format)
735     decoder_sys_t *p_sys = p_dec->p_sys;
737     if (p_sys->session != nil)
738     {
739         VTDecompressionSessionInvalidate(p_sys->session);
740         CFRelease(p_sys->session);
741         p_sys->session = nil;
743         if (b_reset_format)
744         {
745             p_sys->b_format_propagated = false;
746             p_dec->fmt_out.i_codec = 0;
747         }
748         DrainDPB( p_dec );
749     }
751     if (p_sys->videoFormatDescription != nil) {
752         CFRelease(p_sys->videoFormatDescription);
753         p_sys->videoFormatDescription = nil;
754     }
755     if (p_sys->decoderConfiguration != nil) {
756         CFRelease(p_sys->decoderConfiguration);
757         p_sys->decoderConfiguration = nil;
758     }
759     if (p_sys->destinationPixelBufferAttributes != nil) {
760         CFRelease(p_sys->destinationPixelBufferAttributes);
761         p_sys->destinationPixelBufferAttributes = nil;
762     }
765 static int RestartVideoToolbox(decoder_t *p_dec, bool b_reset_format)
767     decoder_sys_t *p_sys = p_dec->p_sys;
769     msg_Dbg(p_dec, "Restarting decoder session");
771     if (p_sys->session != nil)
772         StopVideoToolbox(p_dec, b_reset_format);
774     return StartVideoToolbox(p_dec);
777 #pragma mark - module open and close
779 static int SetupDecoderExtradata(decoder_t *p_dec)
781     decoder_sys_t *p_sys = p_dec->p_sys;
782     CFMutableDictionaryRef extradata_info = NULL;
784     if (p_sys->codec == kCMVideoCodecType_H264)
785     {
786         hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec),
787                          p_dec->fmt_in.i_codec, true);
788         int i_ret = hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in.p_extra,
789                                           p_dec->fmt_in.i_extra);
790         if (i_ret != VLC_SUCCESS)
791             return i_ret;
792         assert(p_sys->hh.pf_process_block != NULL);
794         if (p_dec->fmt_in.p_extra)
795         {
796             int i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"),
797                                             p_dec->fmt_in.p_extra,
798                                             p_dec->fmt_in.i_extra);
799             if (i_ret != VLC_SUCCESS)
800                 return i_ret;
801         }
802         /* else: AnnexB case, we'll get extradata from first input blocks */
803     }
804     else if (p_sys->codec == kCMVideoCodecType_MPEG4Video)
805     {
806         if (!p_dec->fmt_in.i_extra)
807             return VLC_EGENERIC;
808         int i_ret = ESDSCreate(p_dec, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra);
809         if (i_ret != VLC_SUCCESS)
810             return i_ret;
811     }
812     else
813     {
814         int i_ret = ExtradataInfoCreate(p_dec, NULL, NULL, 0);
815         if (i_ret != VLC_SUCCESS)
816             return i_ret;
817     }
819     return VLC_SUCCESS;
822 static int OpenDecoder(vlc_object_t *p_this)
824     decoder_t *p_dec = (decoder_t *)p_this;
826 #if TARGET_OS_IPHONE
827     if (unlikely([[UIDevice currentDevice].systemVersion floatValue] < 8.0)) {
828         msg_Warn(p_dec, "decoder skipped as OS is too old");
829         return VLC_EGENERIC;
830     }
831 #endif
833     if (p_dec->fmt_in.i_cat != VIDEO_ES)
834         return VLC_EGENERIC;
836     /* Fail if this module already failed to decode this ES */
837     if (var_Type(p_dec, "videotoolbox-failed") != 0)
838         return VLC_EGENERIC;
840     /* check quickly if we can digest the offered data */
841     CMVideoCodecType codec;
842     codec = CodecPrecheck(p_dec);
843     if (codec == -1)
844         return VLC_EGENERIC;
846     /* now that we see a chance to decode anything, allocate the
847      * internals and start the decoding session */
848     decoder_sys_t *p_sys;
849     p_sys = malloc(sizeof(*p_sys));
850     if (!p_sys)
851         return VLC_ENOMEM;
852     p_dec->p_sys = p_sys;
853     p_sys->session = nil;
854     p_sys->b_vt_feed = false;
855     p_sys->b_vt_flush = false;
856     p_sys->codec = codec;
857     p_sys->videoFormatDescription = nil;
858     p_sys->decoderConfiguration = nil;
859     p_sys->destinationPixelBufferAttributes = nil;
860     p_sys->extradataInfo = nil;
861     p_sys->p_pic_reorder = NULL;
862     p_sys->i_pic_reorder = 0;
863     p_sys->i_pic_reorder_max = 4;
864     p_sys->b_invalid_pic_reorder_max = false;
865     p_sys->b_poc_based_reorder = false;
866     p_sys->b_format_propagated = false;
867     p_sys->b_abort = false;
868     p_sys->b_enable_temporal_processing = false;
869     h264_poc_context_init( &p_sys->pocctx );
870     vlc_mutex_init(&p_sys->lock);
872     /* return our proper VLC internal state */
873     p_dec->fmt_out.i_cat = p_dec->fmt_in.i_cat;
874     p_dec->fmt_out.video = p_dec->fmt_in.video;
875     if (!p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den)
876     {
877         p_dec->fmt_out.video.i_sar_num = 1;
878         p_dec->fmt_out.video.i_sar_den = 1;
879     }
880     if (!p_dec->fmt_out.video.i_visible_width
881      || !p_dec->fmt_out.video.i_visible_height)
882     {
883         p_dec->fmt_out.video.i_visible_width = p_dec->fmt_out.video.i_width;
884         p_dec->fmt_out.video.i_visible_height = p_dec->fmt_out.video.i_height;
885     }
886     else
887     {
888         p_dec->fmt_out.video.i_width = p_dec->fmt_out.video.i_visible_width;
889         p_dec->fmt_out.video.i_height = p_dec->fmt_out.video.i_visible_height;
890     }
892     p_dec->fmt_out.i_codec = 0;
894     if( codec == kCMVideoCodecType_H264 )
895         p_sys->b_poc_based_reorder = true;
897     int i_ret = SetupDecoderExtradata(p_dec);
898     if (i_ret != VLC_SUCCESS)
899         goto error;
901     if (p_sys->extradataInfo != nil)
902     {
903         i_ret = StartVideoToolbox(p_dec);
904         if (i_ret != VLC_SUCCESS)
905             goto error;
906     } /* else: late opening */
908     p_dec->pf_decode = DecodeBlock;
909     p_dec->pf_flush  = Flush;
911     msg_Info(p_dec, "Using Video Toolbox to decode '%4.4s'", (char *)&p_dec->fmt_in.i_codec);
913     return VLC_SUCCESS;
915 error:
916     CloseDecoder(p_this);
917     return i_ret;
920 static void CloseDecoder(vlc_object_t *p_this)
922     decoder_t *p_dec = (decoder_t *)p_this;
923     decoder_sys_t *p_sys = p_dec->p_sys;
925     StopVideoToolbox(p_dec, true);
927     if (p_sys->codec == kCMVideoCodecType_H264)
928         hxxx_helper_clean(&p_sys->hh);
930     vlc_mutex_destroy(&p_sys->lock);
931     free(p_sys);
934 #pragma mark - helpers
936 static BOOL deviceSupportsAdvancedProfiles()
938 #if TARGET_IPHONE_SIMULATOR
939     return NO;
940 #endif
941 #if TARGET_OS_IPHONE
942     size_t size;
943     cpu_type_t type;
945     size = sizeof(type);
946     sysctlbyname("hw.cputype", &type, &size, NULL, 0);
948     /* Support for H264 profile HIGH 10 was introduced with the first 64bit Apple ARM SoC, the A7 */
949     if (type == CPU_TYPE_ARM64)
950         return YES;
952     return NO;
953 #else
954     return NO;
955 #endif
958 static BOOL deviceSupportsAdvancedLevels()
960 #if TARGET_IPHONE_SIMULATOR
961     return YES;
962 #endif
963 #if TARGET_OS_IPHONE
964     size_t size;
965     int32_t cpufamily;
967     size = sizeof(cpufamily);
968     sysctlbyname("hw.cpufamily", &cpufamily, &size, NULL, 0);
970     /* Proper 4K decoding requires a Twister SoC
971      * Everything below will kill the decoder daemon */
972     if (cpufamily == CPUFAMILY_ARM_TWISTER) {
973         return YES;
974     }
976     return NO;
977 #else
978     return YES;
979 #endif
982 static inline void bo_add_mp4_tag_descr(bo_t *p_bo, uint8_t tag, uint32_t size)
984     bo_add_8(p_bo, tag);
985     for (int i = 3; i>0; i--)
986         bo_add_8(p_bo, (size>>(7*i)) | 0x80);
987     bo_add_8(p_bo, size & 0x7F);
990 static int ESDSCreate(decoder_t *p_dec, uint8_t *p_buf, uint32_t i_buf_size)
992     int full_size = 3 + 5 +13 + 5 + i_buf_size + 3;
993     int config_size = 13 + 5 + i_buf_size;
994     int padding = 12;
996     bo_t bo;
997     bool status = bo_init(&bo, 1024);
998     if (status != true)
999         return VLC_EGENERIC;
1001     bo_add_8(&bo, 0);       // Version
1002     bo_add_24be(&bo, 0);    // Flags
1004     // elementary stream description tag
1005     bo_add_mp4_tag_descr(&bo, 0x03, full_size);
1006     bo_add_16be(&bo, 0);    // esid
1007     bo_add_8(&bo, 0);       // stream priority (0-3)
1009     // decoder configuration description tag
1010     bo_add_mp4_tag_descr(&bo, 0x04, config_size);
1011     bo_add_8(&bo, 32);      // object type identification (32 == MPEG4)
1012     bo_add_8(&bo, 0x11);    // stream type
1013     bo_add_24be(&bo, 0);    // buffer size
1014     bo_add_32be(&bo, 0);    // max bitrate
1015     bo_add_32be(&bo, 0);    // avg bitrate
1017     // decoder specific description tag
1018     bo_add_mp4_tag_descr(&bo, 0x05, i_buf_size);
1019     bo_add_mem(&bo, i_buf_size, p_buf);
1021     // sync layer configuration description tag
1022     bo_add_8(&bo, 0x06);    // tag
1023     bo_add_8(&bo, 0x01);    // length
1024     bo_add_8(&bo, 0x02);    // no SL
1026     int i_ret = ExtradataInfoCreate(p_dec, CFSTR("esds"), bo.b->p_buffer,
1027                                     bo.b->i_buffer);
1028     bo_deinit(&bo);
1029     return i_ret;
1032 static int avcCFromAnnexBCreate(decoder_t *p_dec)
1034     decoder_sys_t *p_sys = p_dec->p_sys;
1036     if (p_sys->hh.h264.i_sps_count == 0 || p_sys->hh.h264.i_pps_count == 0)
1037         return VLC_EGENERIC;
1039     unsigned i_h264_width, i_h264_height, i_video_width, i_video_height;
1040     int i_sar_num, i_sar_den, i_ret;
1041     i_ret = h264_helper_get_current_picture_size(&p_sys->hh,
1042                                                  &i_h264_width, &i_h264_height,
1043                                                  &i_video_width, &i_video_height);
1044     if (i_ret != VLC_SUCCESS)
1045         return i_ret;
1046     i_ret = h264_helper_get_current_sar(&p_sys->hh, &i_sar_num, &i_sar_den);
1047     if (i_ret != VLC_SUCCESS)
1048         return i_ret;
1050     p_dec->fmt_out.video.i_visible_width =
1051     p_dec->fmt_out.video.i_width = i_video_width;
1052     p_dec->fmt_out.video.i_visible_height =
1053     p_dec->fmt_out.video.i_height = i_video_height;
1054     p_dec->fmt_out.video.i_sar_num = i_sar_num;
1055     p_dec->fmt_out.video.i_sar_den = i_sar_den;
1057     block_t *p_avcC = h264_helper_get_avcc_config(&p_sys->hh);
1058     if (!p_avcC)
1059         return VLC_EGENERIC;
1061     i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"), p_avcC->p_buffer,
1062                                 p_avcC->i_buffer);
1063     block_Release(p_avcC);
1064     return i_ret;
1067 static int ExtradataInfoCreate(decoder_t *p_dec, CFStringRef name, void *p_data,
1068                                size_t i_data)
1070     decoder_sys_t *p_sys = p_dec->p_sys;
1072     p_sys->extradataInfo =
1073         CFDictionaryCreateMutable(kCFAllocatorDefault, 1,
1074                                   &kCFTypeDictionaryKeyCallBacks,
1075                                   &kCFTypeDictionaryValueCallBacks);
1076     if (p_sys->extradataInfo == nil)
1077         return VLC_EGENERIC;
1079     if (p_data == NULL)
1080         return VLC_SUCCESS;
1082     CFDataRef extradata = CFDataCreate(kCFAllocatorDefault, p_data, i_data);
1083     if (extradata == nil)
1084     {
1085         CFRelease(p_sys->extradataInfo);
1086         p_sys->extradataInfo = nil;
1087         return VLC_EGENERIC;
1088     }
1089     CFDictionarySetValue(p_sys->extradataInfo, name, extradata);
1090     CFRelease(extradata);
1091     return VLC_SUCCESS;
1094 static CMSampleBufferRef VTSampleBufferCreate(decoder_t *p_dec,
1095                                               CMFormatDescriptionRef fmt_desc,
1096                                               block_t *p_block)
1098     OSStatus status;
1099     CMBlockBufferRef  block_buf = NULL;
1100     CMSampleBufferRef sample_buf = NULL;
1101     CMTime pts;
1102     if(!p_dec->p_sys->b_poc_based_reorder && p_block->i_pts == VLC_TS_INVALID)
1103         pts = CMTimeMake(p_block->i_dts, CLOCK_FREQ);
1104     else
1105         pts = CMTimeMake(p_block->i_pts, CLOCK_FREQ);
1107     CMSampleTimingInfo timeInfoArray[1] = { {
1108         .duration = CMTimeMake(p_block->i_length, 1),
1109         .presentationTimeStamp = pts,
1110         .decodeTimeStamp = CMTimeMake(p_block->i_dts, CLOCK_FREQ),
1111     } };
1113     status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,// structureAllocator
1114                                                 p_block->p_buffer,  // memoryBlock
1115                                                 p_block->i_buffer,  // blockLength
1116                                                 kCFAllocatorNull,   // blockAllocator
1117                                                 NULL,               // customBlockSource
1118                                                 0,                  // offsetToData
1119                                                 p_block->i_buffer,  // dataLength
1120                                                 false,              // flags
1121                                                 &block_buf);
1123     if (!status) {
1124         status = CMSampleBufferCreate(kCFAllocatorDefault,  // allocator
1125                                       block_buf,            // dataBuffer
1126                                       TRUE,                 // dataReady
1127                                       0,                    // makeDataReadyCallback
1128                                       0,                    // makeDataReadyRefcon
1129                                       fmt_desc,             // formatDescription
1130                                       1,                    // numSamples
1131                                       1,                    // numSampleTimingEntries
1132                                       timeInfoArray,        // sampleTimingArray
1133                                       0,                    // numSampleSizeEntries
1134                                       NULL,                 // sampleSizeArray
1135                                       &sample_buf);
1136         if (status != noErr)
1137             msg_Warn(p_dec, "sample buffer creation failure %i", (int)status);
1138     } else
1139         msg_Warn(p_dec, "cm block buffer creation failure %i", (int)status);
1141     if (block_buf != nil)
1142         CFRelease(block_buf);
1143     block_buf = nil;
1145     return sample_buf;
1148 void VTDictionarySetInt32(CFMutableDictionaryRef dict, CFStringRef key, int value)
1150     CFNumberRef number;
1151     number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
1152     CFDictionarySetValue(dict, key, number);
1153     CFRelease(number);
1156 static void copy420YpCbCr8Planar(picture_t *p_pic,
1157                                  CVPixelBufferRef buffer,
1158                                  unsigned i_width,
1159                                  unsigned i_height)
1161     uint8_t *pp_plane[2];
1162     size_t pi_pitch[2];
1164     if (!buffer || i_width == 0 || i_height == 0)
1165         return;
1167     CVPixelBufferLockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
1169     for (int i = 0; i < 2; i++) {
1170         pp_plane[i] = CVPixelBufferGetBaseAddressOfPlane(buffer, i);
1171         pi_pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(buffer, i);
1172     }
1174     CopyFromNv12ToI420(p_pic, pp_plane, pi_pitch, i_height);
1176     CVPixelBufferUnlockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
1179 static int HandleVTStatus(decoder_t *p_dec, OSStatus status)
1181 #define VTERRCASE(x) \
1182     case x: msg_Err(p_dec, "vt session error: '" #x "'"); break;
1184     switch (status)
1185     {
1186         case 0:
1187             return VLC_SUCCESS;
1189         VTERRCASE(kVTPropertyNotSupportedErr)
1190         VTERRCASE(kVTPropertyReadOnlyErr)
1191         VTERRCASE(kVTParameterErr)
1192         VTERRCASE(kVTInvalidSessionErr)
1193         VTERRCASE(kVTAllocationFailedErr)
1194         VTERRCASE(kVTPixelTransferNotSupportedErr)
1195         VTERRCASE(kVTCouldNotFindVideoDecoderErr)
1196         VTERRCASE(kVTCouldNotCreateInstanceErr)
1197         VTERRCASE(kVTCouldNotFindVideoEncoderErr)
1198         VTERRCASE(kVTVideoDecoderBadDataErr)
1199         VTERRCASE(kVTVideoDecoderUnsupportedDataFormatErr)
1200         VTERRCASE(kVTVideoDecoderMalfunctionErr)
1201         VTERRCASE(kVTVideoEncoderMalfunctionErr)
1202         VTERRCASE(kVTVideoDecoderNotAvailableNowErr)
1203         VTERRCASE(kVTImageRotationNotSupportedErr)
1204         VTERRCASE(kVTVideoEncoderNotAvailableNowErr)
1205         VTERRCASE(kVTFormatDescriptionChangeNotSupportedErr)
1206         VTERRCASE(kVTInsufficientSourceColorDataErr)
1207         VTERRCASE(kVTCouldNotCreateColorCorrectionDataErr)
1208         VTERRCASE(kVTColorSyncTransformConvertFailedErr)
1209         VTERRCASE(kVTVideoDecoderAuthorizationErr)
1210         VTERRCASE(kVTVideoEncoderAuthorizationErr)
1211         VTERRCASE(kVTColorCorrectionPixelTransferFailedErr)
1212         VTERRCASE(kVTMultiPassStorageIdentifierMismatchErr)
1213         VTERRCASE(kVTMultiPassStorageInvalidErr)
1214         VTERRCASE(kVTFrameSiloInvalidTimeStampErr)
1215         VTERRCASE(kVTFrameSiloInvalidTimeRangeErr)
1216         VTERRCASE(kVTCouldNotFindTemporalFilterErr)
1217         VTERRCASE(kVTPixelTransferNotPermittedErr)
1218         case -12219:
1219             msg_Err(p_dec, "vt session error: "
1220                     "'kVTColorCorrectionImageRotationFailedErr'");
1221             break;
1222         default:
1223             msg_Err(p_dec, "unknown vt session error (%i)", (int)status);
1224     }
1225 #undef VTERRCASE
1226     return VLC_EGENERIC;
1229 #pragma mark - actual decoding
1231 static void Flush(decoder_t *p_dec)
1233     decoder_sys_t *p_sys = p_dec->p_sys;
1235     /* There is no Flush in VT api, ask to restart VT from next DecodeBlock if
1236      * we already feed some input blocks (it's better to not restart here in
1237      * order to avoid useless restart just before a close). */
1238     p_sys->b_vt_flush = p_sys->b_vt_feed;
1241 static void Drain(decoder_t *p_dec)
1243     decoder_sys_t *p_sys = p_dec->p_sys;
1245     /* draining: return last pictures of the reordered queue */
1246     if (p_sys->session)
1247         VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
1249     vlc_mutex_lock(&p_sys->lock);
1250     DrainDPB( p_dec );
1251     vlc_mutex_unlock(&p_sys->lock);
1254 static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
1256     decoder_sys_t *p_sys = p_dec->p_sys;
1258     if (p_sys->b_vt_flush) {
1259         RestartVideoToolbox(p_dec, false);
1260         p_sys->b_vt_flush = false;
1261     }
1263     if (p_block == NULL)
1264     {
1265         Drain(p_dec);
1266         return VLCDEC_SUCCESS;
1267     }
1269     vlc_mutex_lock(&p_sys->lock);
1270     if (p_sys->b_abort) { /* abort from output thread (DecoderCallback) */
1271         vlc_mutex_unlock(&p_sys->lock);
1272         /* Add an empty variable so that videotoolbox won't be loaded again for
1273         * this ES */
1274         var_Create(p_dec, "videotoolbox-failed", VLC_VAR_VOID);
1275         return VLCDEC_RELOAD;
1276     }
1277     vlc_mutex_unlock(&p_sys->lock);
1279     if (unlikely(p_block->i_flags&(BLOCK_FLAG_CORRUPTED)))
1280     {
1281         if (p_sys->b_vt_feed)
1282         {
1283             Drain(p_dec);
1284             RestartVideoToolbox(p_dec, false);
1285         }
1286         goto skip;
1287     }
1289     bool b_config_changed = false;
1290     if (p_sys->codec == kCMVideoCodecType_H264)
1291     {
1292         p_block = p_sys->hh.pf_process_block(&p_sys->hh, p_block, &b_config_changed);
1293         if (!p_block)
1294             return VLCDEC_SUCCESS;
1295     }
1297     if (b_config_changed)
1298     {
1299         /* decoding didn't start yet, which is ok for H264, let's see
1300          * if we can use this block to get going */
1301         assert(p_sys->codec == kCMVideoCodecType_H264);
1302         if (p_sys->session)
1303         {
1304             msg_Dbg(p_dec, "SPS/PPS changed: draining H264 decoder");
1305             Drain(p_dec);
1306             msg_Dbg(p_dec, "SPS/PPS changed: restarting H264 decoder");
1307             StopVideoToolbox(p_dec, true);
1308         }
1310         int i_ret = avcCFromAnnexBCreate(p_dec);
1311         if (i_ret == VLC_SUCCESS)
1312         {
1313             if ((p_block->i_flags & BLOCK_FLAG_INTERLACED_MASK)
1314              && var_InheritBool(p_dec, "videotoolbox-temporal-deinterlacing"))
1315                 p_sys->b_enable_temporal_processing = true;
1317             msg_Dbg(p_dec, "Got SPS/PPS: late opening of H264 decoder");
1318             StartVideoToolbox(p_dec);
1319         }
1320         if (!p_sys->session)
1321             goto skip;
1322     }
1324     frame_info_t *p_info = CreateReorderInfo(p_dec, p_block);
1325     if(unlikely(!p_info))
1326         goto skip;
1328     CMSampleBufferRef sampleBuffer =
1329         VTSampleBufferCreate(p_dec, p_sys->videoFormatDescription, p_block);
1330     if (unlikely(!sampleBuffer))
1331     {
1332         free(p_info);
1333         goto skip;
1334     }
1336     VTDecodeInfoFlags flagOut;
1337     VTDecodeFrameFlags decoderFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
1338     if (unlikely(p_sys->b_enable_temporal_processing))
1339         decoderFlags |= kVTDecodeFrame_EnableTemporalProcessing;
1341     OSStatus status =
1342         VTDecompressionSessionDecodeFrame(p_sys->session, sampleBuffer,
1343                                           decoderFlags, p_info, &flagOut);
1344     if (HandleVTStatus(p_dec, status) == VLC_SUCCESS)
1345         p_sys->b_vt_feed = true;
1346     else
1347     {
1348         bool b_abort = false;
1349         switch (status)
1350         {
1351             case -8960 /* codecErr */:
1352             case kCVReturnInvalidArgument:
1353             case kVTVideoDecoderMalfunctionErr:
1354                 b_abort = true;
1355                 break;
1356             case -8969 /* codecBadDataErr */:
1357             case kVTVideoDecoderBadDataErr:
1358                 if (RestartVideoToolbox(p_dec, true) == VLC_SUCCESS)
1359                 {
1360                     status = VTDecompressionSessionDecodeFrame(p_sys->session,
1361                                     sampleBuffer, decoderFlags, p_info, &flagOut);
1363                     if (status != 0)
1364                     {
1365                         free( p_info );
1366                         b_abort = true;
1367                     }
1368                 }
1369                 break;
1370             case kVTInvalidSessionErr:
1371                 RestartVideoToolbox(p_dec, true);
1372                 break;
1373         }
1374         if (b_abort)
1375         {
1376             msg_Err(p_dec, "decoder failure, Abort.");
1377             /* The decoder module will be reloaded next time since we already
1378              * modified the input block */
1379             vlc_mutex_lock(&p_sys->lock);
1380             p_dec->p_sys->b_abort = true;
1381             vlc_mutex_unlock(&p_sys->lock);
1382         }
1383     }
1384     CFRelease(sampleBuffer);
1386 skip:
1387     block_Release(p_block);
1388     return VLCDEC_SUCCESS;
1391 static int UpdateVideoFormat(decoder_t *p_dec, CVPixelBufferRef imageBuffer)
1393     CFDictionaryRef attachments = CVBufferGetAttachments(imageBuffer, kCVAttachmentMode_ShouldPropagate);
1394     NSDictionary *attachmentDict = (NSDictionary *)attachments;
1395 #ifndef NDEBUG
1396     NSLog(@"%@", attachments);
1397 #endif
1398     if (attachmentDict == nil || attachmentDict.count == 0)
1399         return -1;
1401     NSString *colorSpace = attachmentDict[(NSString *)kCVImageBufferYCbCrMatrixKey];
1402     if (colorSpace != nil) {
1403         if ([colorSpace isEqualToString:(NSString *)kCVImageBufferYCbCrMatrix_ITU_R_601_4])
1404             p_dec->fmt_out.video.space = COLOR_SPACE_BT601;
1405         else if ([colorSpace isEqualToString:(NSString *)kCVImageBufferYCbCrMatrix_ITU_R_709_2])
1406             p_dec->fmt_out.video.space = COLOR_SPACE_BT709;
1407         else
1408             p_dec->fmt_out.video.space = COLOR_SPACE_UNDEF;
1409     }
1411     NSString *colorprimary = attachmentDict[(NSString *)kCVImageBufferColorPrimariesKey];
1412     if (colorprimary != nil) {
1413         if ([colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_SMPTE_C] ||
1414             [colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_EBU_3213])
1415             p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT601_625;
1416         else if ([colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_ITU_R_709_2])
1417             p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT709;
1418         else if ([colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_P22])
1419             p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_DCI_P3;
1420         else
1421             p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_UNDEF;
1422     }
1424     NSString *transfer = attachmentDict[(NSString *)kCVImageBufferTransferFunctionKey];
1425     if (transfer != nil) {
1426         if ([transfer isEqualToString:(NSString *)kCVImageBufferTransferFunction_ITU_R_709_2] ||
1427             [transfer isEqualToString:(NSString *)kCVImageBufferTransferFunction_SMPTE_240M_1995])
1428             p_dec->fmt_out.video.transfer = TRANSFER_FUNC_BT709;
1429         else
1430             p_dec->fmt_out.video.transfer = TRANSFER_FUNC_UNDEF;
1431     }
1433     NSString *chromaLocation = attachmentDict[(NSString *)kCVImageBufferChromaLocationTopFieldKey];
1434     if (chromaLocation != nil) {
1435         if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Left] ||
1436             [chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_DV420])
1437             p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_LEFT;
1438         else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Center])
1439             p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_CENTER;
1440         else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_TopLeft])
1441             p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_TOP_LEFT;
1442         else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Top])
1443             p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_TOP_CENTER;
1444         else
1445             p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_UNDEF;
1446     }
1447     if (p_dec->fmt_out.video.chroma_location == CHROMA_LOCATION_UNDEF) {
1448         chromaLocation = attachmentDict[(NSString *)kCVImageBufferChromaLocationBottomFieldKey];
1449         if (chromaLocation != nil) {
1450             if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_BottomLeft])
1451                 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_BOTTOM_LEFT;
1452             else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Bottom])
1453                 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_BOTTOM_CENTER;
1454         }
1455     }
1457     uint32_t cvfmt = CVPixelBufferGetPixelFormatType(imageBuffer);
1458     switch (cvfmt)
1459     {
1460         case kCVPixelFormatType_422YpCbCr8:
1461         case 'yuv2':
1462             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_UYVY;
1463             assert(CVPixelBufferIsPlanar(imageBuffer) == false);
1464             break;
1465         case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
1466         case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
1467             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_NV12;
1468             assert(CVPixelBufferIsPlanar(imageBuffer) == true);
1469             break;
1470         case kCVPixelFormatType_420YpCbCr8Planar:
1471             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_I420;
1472             assert(CVPixelBufferIsPlanar(imageBuffer) == true);
1473             break;
1474         case kCVPixelFormatType_32BGRA:
1475             p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_BGRA;
1476             assert(CVPixelBufferIsPlanar(imageBuffer) == false);
1477             break;
1478         default:
1479             p_dec->p_sys->b_abort = true;
1480             return -1;
1481     }
1482     return decoder_UpdateVideoFormat(p_dec);
1485 static void DecoderCallback(void *decompressionOutputRefCon,
1486                             void *sourceFrameRefCon,
1487                             OSStatus status,
1488                             VTDecodeInfoFlags infoFlags,
1489                             CVPixelBufferRef imageBuffer,
1490                             CMTime pts,
1491                             CMTime duration)
1493     VLC_UNUSED(duration);
1494     decoder_t *p_dec = (decoder_t *)decompressionOutputRefCon;
1495     decoder_sys_t *p_sys = p_dec->p_sys;
1496     frame_info_t *p_info = (frame_info_t *) sourceFrameRefCon;
1498     if (status != noErr) {
1499         msg_Warn(p_dec, "decoding of a frame failed (%i, %u)", (int)status, (unsigned int) infoFlags);
1500         if( status != kVTVideoDecoderBadDataErr && status != -8969 )
1501             free(p_info);
1502         return;
1503     }
1504     assert(imageBuffer);
1506     if (unlikely(!p_sys->b_format_propagated)) {
1507         vlc_mutex_lock(&p_sys->lock);
1508         p_sys->b_format_propagated =
1509             UpdateVideoFormat(p_dec, imageBuffer) == VLC_SUCCESS;
1510         vlc_mutex_unlock(&p_sys->lock);
1512         if (!p_sys->b_format_propagated)
1513         {
1514             free(p_info);
1515             return;
1516         }
1517         assert(p_dec->fmt_out.i_codec != 0);
1518     }
1520     if (infoFlags & kVTDecodeInfo_FrameDropped)
1521     {
1522         /* We can't trust VT, some decoded frames can be marked as dropped */
1523         msg_Dbg(p_dec, "decoder dropped frame");
1524     }
1526     if (!CMTIME_IS_VALID(pts))
1527     {
1528         free(p_info);
1529         return;
1530     }
1532     if (CVPixelBufferGetDataSize(imageBuffer) == 0)
1533     {
1534         free(p_info);
1535         return;
1536     }
1538     if(likely(p_info))
1539     {
1540         picture_t *p_pic = decoder_NewPicture(p_dec);
1541         if (!p_pic)
1542         {
1543             free(p_info);
1544             return;
1545         }
1547         if (unlikely(!p_pic->p_sys)) {
1548             vlc_mutex_lock(&p_sys->lock);
1549             p_dec->p_sys->b_abort = true;
1550             vlc_mutex_unlock(&p_sys->lock);
1551             picture_Release(p_pic);
1552             free(p_info);
1553             return;
1554         }
1556         /* Can happen if the pic was discarded */
1557         if (p_pic->p_sys->pixelBuffer != nil)
1558             CFRelease(p_pic->p_sys->pixelBuffer);
1560         /* will be freed by the vout */
1561         p_pic->p_sys->pixelBuffer = CVPixelBufferRetain(imageBuffer);
1563         p_info->p_picture = p_pic;
1565         p_pic->date = pts.value;
1566         p_pic->b_progressive = p_info->b_progressive;
1567         if(!p_pic->b_progressive)
1568         {
1569             p_pic->i_nb_fields = p_info->i_num_ts;
1570             p_pic->b_top_field_first = p_info->b_top_field_first;
1571         }
1573         vlc_mutex_lock(&p_sys->lock);
1574         OnDecodedFrame( p_dec, p_info );
1575         vlc_mutex_unlock(&p_sys->lock);
1576     }
1578     return;