videotoolbox.m 44 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*****************************************************************************
 * videotoolbox.m: Video Toolbox decoder
 *****************************************************************************
 * Copyright © 2014-2015 VideoLabs SAS
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#pragma mark preamble

#ifdef HAVE_CONFIG_H
# import "config.h"
#endif

#import <vlc_common.h>
#import <vlc_plugin.h>
#import <vlc_codec.h>
32
#import "hxxx_helper.h"
33
34
#import "../video_chroma/copy.h"
#import <vlc_bits.h>
35
#import <vlc_boxes.h>
36
37

#import <VideoToolbox/VideoToolbox.h>
38
#import <VideoToolbox/VTErrors.h>
39
40
41
42
43
44
45
46

#import <Foundation/Foundation.h>
#import <TargetConditionals.h>

#import <sys/types.h>
#import <sys/sysctl.h>
#import <mach/machine.h>

47
48
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
49
50
51
52
53
54

/* support iOS SDKs < v9.1 */
#ifndef CPUFAMILY_ARM_TWISTER
#define CPUFAMILY_ARM_TWISTER 0x92fb37c8
#endif

55
56
#endif

57
58
59
60
61
#pragma mark - module descriptor

static int OpenDecoder(vlc_object_t *);
static void CloseDecoder(vlc_object_t *);

62
#if MAC_OS_X_VERSION_MIN_ALLOWED < 1090
63
64
65
66
67
const CFStringRef kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder = CFSTR("EnableHardwareAcceleratedVideoDecoder");
const CFStringRef kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder = CFSTR("RequireHardwareAcceleratedVideoDecoder");
#endif

#define VT_REQUIRE_HW_DEC N_("Use Hardware decoders only")
68
69
#define VT_TEMPO_DEINTERLACE N_("Deinterlacing")
#define VT_TEMPO_DEINTERLACE_LONG N_("If interlaced content is detected, temporal deinterlacing is enabled at the expense of a pipeline delay.")
70
71
72
73
74
75
76

vlc_module_begin()
set_category(CAT_INPUT)
set_subcategory(SUBCAT_INPUT_VCODEC)
set_description(N_("VideoToolbox video decoder"))
set_capability("decoder",800)
set_callbacks(OpenDecoder, CloseDecoder)
77

78
add_bool("videotoolbox-temporal-deinterlacing", true, VT_TEMPO_DEINTERLACE, VT_TEMPO_DEINTERLACE_LONG, false)
79
80
81
82
83
add_bool("videotoolbox-hw-decoder-only", false, VT_REQUIRE_HW_DEC, VT_REQUIRE_HW_DEC, false)
vlc_module_end()

#pragma mark - local prototypes

84
static int ESDSCreate(decoder_t *, uint8_t *, uint32_t);
85
static int avcCFromAnnexBCreate(decoder_t *);
86
static int ExtradataInfoCreate(decoder_t *, CFStringRef, void *, size_t);
87
static int HandleVTStatus(decoder_t *, OSStatus);
88
static int DecodeBlock(decoder_t *, block_t *);
89
90
91
static void PicReorder_pushSorted(decoder_t *, picture_t *);
static picture_t *PicReorder_pop(decoder_t *, bool);
static void PicReorder_flush(decoder_t *);
92
static void PicReorder_setup(decoder_t *);
93
static void Flush(decoder_t *);
94
static void DecoderCallback(void *, void *, OSStatus, VTDecodeInfoFlags,
95
                            CVPixelBufferRef, CMTime, CMTime);
96
97
98
99
void VTDictionarySetInt32(CFMutableDictionaryRef, CFStringRef, int);
static void copy420YpCbCr8Planar(picture_t *, CVPixelBufferRef buffer,
                                 unsigned i_width, unsigned i_height);
static BOOL deviceSupportsAdvancedProfiles();
100
static BOOL deviceSupportsAdvancedLevels();
101

102
103
104
105
struct picture_sys_t {
    CFTypeRef pixelBuffer;
};

106
107
108
109
110
#pragma mark - decoder structure

struct decoder_sys_t
{
    CMVideoCodecType            codec;
111
    struct                      hxxx_helper hh;
112

113
114
    bool                        b_vt_feed;
    bool                        b_vt_flush;
115
116
    VTDecompressionSessionRef   session;
    CMVideoFormatDescriptionRef videoFormatDescription;
117
118
    CFMutableDictionaryRef      decoderConfiguration;
    CFMutableDictionaryRef      destinationPixelBufferAttributes;
119
    CFMutableDictionaryRef      extradataInfo;
120

121
122
123
    vlc_mutex_t                 lock;
    picture_t                   *p_pic_reorder;
    size_t                      i_pic_reorder;
124
    size_t                      i_pic_reorder_max;
125
    bool                        b_enable_temporal_processing;
126
127

    bool                        b_format_propagated;
128
    bool                        b_abort;
129
130
131
132
133
134
};

#pragma mark - start & stop

static CMVideoCodecType CodecPrecheck(decoder_t *p_dec)
{
135
    uint8_t i_profile = 0xFF, i_level = 0xFF;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
    bool b_ret = false;
    CMVideoCodecType codec;

    /* check for the codec we can and want to decode */
    switch (p_dec->fmt_in.i_codec) {
        case VLC_CODEC_H264:
            codec = kCMVideoCodecType_H264;

            b_ret = h264_get_profile_level(&p_dec->fmt_in, &i_profile, &i_level, NULL);
            if (!b_ret) {
                msg_Warn(p_dec, "H264 profile and level parsing failed because it didn't arrive yet");
                return kCMVideoCodecType_H264;
            }

150
            msg_Dbg(p_dec, "trying to decode MPEG-4 Part 10: profile %" PRIx8 ", level %" PRIx8, i_profile, i_level);
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

            switch (i_profile) {
                case PROFILE_H264_BASELINE:
                case PROFILE_H264_MAIN:
                case PROFILE_H264_HIGH:
                    break;

                case PROFILE_H264_HIGH_10:
                {
                    if (deviceSupportsAdvancedProfiles())
                        break;
                }

                default:
                {
166
                    msg_Dbg(p_dec, "unsupported H264 profile %" PRIx8, i_profile);
167
168
169
170
171
172
173
                    return -1;
                }
            }

#if !TARGET_OS_IPHONE
            /* a level higher than 5.2 was not tested, so don't dare to
             * try to decode it*/
174
            if (i_level > 52) {
175
                msg_Dbg(p_dec, "unsupported H264 level %" PRIx8, i_level);
176
                return -1;
177
            }
178
179
#else
            /* on SoC A8, 4.2 is the highest specified profile */
180
181
182
            if (i_level > 42) {
                /* on Twister, we can do up to 5.2 */
                if (!deviceSupportsAdvancedLevels() || i_level > 52) {
183
                    msg_Dbg(p_dec, "unsupported H264 level %" PRIx8, i_level);
184
185
186
                    return -1;
                }
            }
187
188
189
190
#endif

            break;
        case VLC_CODEC_MP4V:
191
192
193
194
195
196
197
        {
            if (p_dec->fmt_in.i_original_fourcc == VLC_FOURCC( 'X','V','I','D' )) {
                msg_Warn(p_dec, "XVID decoding not implemented, fallback on software");
                return -1;
            }

            msg_Dbg(p_dec, "Will decode MP4V with original FourCC '%4.4s'", (char *)&p_dec->fmt_in.i_original_fourcc);
198
            codec = kCMVideoCodecType_MPEG4Video;
199

200
            break;
201
        }
202
#if !TARGET_OS_IPHONE
203
204
205
206
        case VLC_CODEC_H263:
            codec = kCMVideoCodecType_H263;
            break;

207
            /* there are no DV or ProRes decoders on iOS, so bailout early */
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
        case VLC_CODEC_PRORES:
            /* the VT decoder can't differenciate between the ProRes flavors, so we do it */
            switch (p_dec->fmt_in.i_original_fourcc) {
                case VLC_FOURCC( 'a','p','4','c' ):
                case VLC_FOURCC( 'a','p','4','h' ):
                    codec = kCMVideoCodecType_AppleProRes4444;
                    break;

                case VLC_FOURCC( 'a','p','c','h' ):
                    codec = kCMVideoCodecType_AppleProRes422HQ;
                    break;

                case VLC_FOURCC( 'a','p','c','s' ):
                    codec = kCMVideoCodecType_AppleProRes422LT;
                    break;

                case VLC_FOURCC( 'a','p','c','o' ):
                    codec = kCMVideoCodecType_AppleProRes422Proxy;
                    break;

                default:
                    codec = kCMVideoCodecType_AppleProRes422;
                    break;
            }
            if (codec != 0)
                break;

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
        case VLC_CODEC_DV:
            /* the VT decoder can't differenciate between PAL and NTSC, so we need to do it */
            switch (p_dec->fmt_in.i_original_fourcc) {
                case VLC_FOURCC( 'd', 'v', 'c', ' '):
                case VLC_FOURCC( 'd', 'v', ' ', ' '):
                    msg_Dbg(p_dec, "Decoding DV NTSC");
                    codec = kCMVideoCodecType_DVCNTSC;
                    break;

                case VLC_FOURCC( 'd', 'v', 's', 'd'):
                case VLC_FOURCC( 'd', 'v', 'c', 'p'):
                case VLC_FOURCC( 'D', 'V', 'S', 'D'):
                    msg_Dbg(p_dec, "Decoding DV PAL");
                    codec = kCMVideoCodecType_DVCPAL;
                    break;

                default:
                    break;
            }
            if (codec != 0)
                break;
#endif
257
258
            /* mpgv / mp2v needs fixing, so disable it for now */
#if 0
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
        case VLC_CODEC_MPGV:
            codec = kCMVideoCodecType_MPEG1Video;
            break;
        case VLC_CODEC_MP2V:
            codec = kCMVideoCodecType_MPEG2Video;
            break;
#endif

        default:
#ifndef NDEBUG
            msg_Err(p_dec, "'%4.4s' is not supported", (char *)&p_dec->fmt_in.i_codec);
#endif
            return -1;
    }

    return codec;
}

277
static int StartVideoToolbox(decoder_t *p_dec)
278
279
280
281
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    OSStatus status;

282
283
284
285
286
287
288
289
290
    assert(p_sys->extradataInfo != nil);

    p_sys->decoderConfiguration =
        CFDictionaryCreateMutable(kCFAllocatorDefault, 2,
                                  &kCFTypeDictionaryKeyCallBacks,
                                  &kCFTypeDictionaryValueCallBacks);
    if (p_sys->decoderConfiguration == NULL)
        return VLC_ENOMEM;

291
    CFDictionarySetValue(p_sys->decoderConfiguration,
292
293
                         kCVImageBufferChromaLocationBottomFieldKey,
                         kCVImageBufferChromaLocation_Left);
294
    CFDictionarySetValue(p_sys->decoderConfiguration,
295
296
297
                         kCVImageBufferChromaLocationTopFieldKey,
                         kCVImageBufferChromaLocation_Left);

298
299
300
    CFDictionarySetValue(p_sys->decoderConfiguration,
                         kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms,
                         p_sys->extradataInfo);
301
302

    /* pixel aspect ratio */
303
304
305
306
307
308
309
310
311
    CFMutableDictionaryRef pixelaspectratio =
        CFDictionaryCreateMutable(kCFAllocatorDefault, 2,
                                  &kCFTypeDictionaryKeyCallBacks,
                                  &kCFTypeDictionaryValueCallBacks);

    const unsigned i_video_width = p_dec->fmt_out.video.i_width;
    const unsigned i_video_height = p_dec->fmt_out.video.i_height;
    const unsigned i_sar_num = p_dec->fmt_out.video.i_sar_num;
    const unsigned i_sar_den = p_dec->fmt_out.video.i_sar_den;
312
313
314
315
316
317
318

    VTDictionarySetInt32(pixelaspectratio,
                         kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
                         i_sar_num);
    VTDictionarySetInt32(pixelaspectratio,
                         kCVImageBufferPixelAspectRatioVerticalSpacingKey,
                         i_sar_den);
319
    CFDictionarySetValue(p_sys->decoderConfiguration,
320
321
322
323
324
325
326
                         kCVImageBufferPixelAspectRatioKey,
                         pixelaspectratio);
    CFRelease(pixelaspectratio);

    /* enable HW accelerated playback, since this is optional on OS X
     * note that the backend may still fallback on software mode if no
     * suitable hardware is available */
327
    CFDictionarySetValue(p_sys->decoderConfiguration,
328
329
330
331
332
333
                         kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
                         kCFBooleanTrue);

    /* on OS X, we can force VT to fail if no suitable HW decoder is available,
     * preventing the aforementioned SW fallback */
    if (var_InheritInteger(p_dec, "videotoolbox-hw-decoder-only"))
334
        CFDictionarySetValue(p_sys->decoderConfiguration,
335
336
337
                             kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder,
                             kCFBooleanTrue);

338
339
340
341
342
343
344
345
346
    if (p_sys->b_enable_temporal_processing)
    {
        msg_Dbg(p_dec, "Interlaced content detected, inserting temporal deinterlacer");
        CFDictionarySetValue(p_sys->decoderConfiguration,
                             kVTDecompressionPropertyKey_FieldMode,
                             kVTDecompressionProperty_FieldMode_DeinterlaceFields);
        CFDictionarySetValue(p_sys->decoderConfiguration,
                             kVTDecompressionPropertyKey_DeinterlaceMode,
                             kVTDecompressionProperty_DeinterlaceMode_Temporal);
347
348
    }

349
350
351
352
353
    /* create video format description */
    status = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
                                            p_sys->codec,
                                            i_video_width,
                                            i_video_height,
354
                                            p_sys->decoderConfiguration,
355
356
                                            &p_sys->videoFormatDescription);
    if (status) {
357
        CFRelease(p_sys->decoderConfiguration);
358
        msg_Err(p_dec, "video format description creation failed (%i)", (int)status);
359
360
361
362
        return VLC_EGENERIC;
    }

    /* destination pixel buffer attributes */
363
364
365
366
    p_sys->destinationPixelBufferAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault,
                                                                        2,
                                                                        &kCFTypeDictionaryKeyCallBacks,
                                                                        &kCFTypeDictionaryValueCallBacks);
367

368
#if !TARGET_OS_IPHONE
369
    CFDictionarySetValue(p_sys->destinationPixelBufferAttributes,
370
                         kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey,
371
                         kCFBooleanTrue);
372
#else
373
    CFDictionarySetValue(p_sys->destinationPixelBufferAttributes,
374
                         kCVPixelBufferOpenGLESCompatibilityKey,
375
                         kCFBooleanTrue);
376
#endif
377

378
    VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
379
380
                         kCVPixelBufferWidthKey,
                         i_video_width);
381
    VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
382
383
                         kCVPixelBufferHeightKey,
                         i_video_height);
384
    VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
385
386
387
                         kCVPixelBufferBytesPerRowAlignmentKey,
                         i_video_width * 2);

388
389
390
391
    /* setup decoder callback record */
    VTDecompressionOutputCallbackRecord decoderCallbackRecord;
    decoderCallbackRecord.decompressionOutputCallback = DecoderCallback;
    decoderCallbackRecord.decompressionOutputRefCon = p_dec;
392

393
394
395
396
397
398
    /* create decompression session */
    status = VTDecompressionSessionCreate(kCFAllocatorDefault,
                                          p_sys->videoFormatDescription,
                                          p_sys->decoderConfiguration,
                                          p_sys->destinationPixelBufferAttributes,
                                          &decoderCallbackRecord, &p_sys->session);
399

400
401
    if (HandleVTStatus(p_dec, status) != VLC_SUCCESS)
        return VLC_EGENERIC;
402

403
    return VLC_SUCCESS;
404
405
}

406
static void StopVideoToolbox(decoder_t *p_dec, bool b_reset_format)
407
408
409
{
    decoder_sys_t *p_sys = p_dec->p_sys;

410
    if (p_sys->session != nil)
411
412
413
414
415
416
417
418
419
420
421
422
423
    {
        VTDecompressionSessionInvalidate(p_sys->session);
        CFRelease(p_sys->session);
        p_sys->session = nil;

        if (b_reset_format)
        {
            p_sys->b_format_propagated = false;
            p_dec->fmt_out.i_codec = 0;
        }

        PicReorder_flush(p_dec);
    }
424

425
    if (p_sys->videoFormatDescription != nil) {
426
        CFRelease(p_sys->videoFormatDescription);
427
428
        p_sys->videoFormatDescription = nil;
    }
429
430
431
432
433
434
435
436
    if (p_sys->decoderConfiguration != nil) {
        CFRelease(p_sys->decoderConfiguration);
        p_sys->decoderConfiguration = nil;
    }
    if (p_sys->destinationPixelBufferAttributes != nil) {
        CFRelease(p_sys->destinationPixelBufferAttributes);
        p_sys->destinationPixelBufferAttributes = nil;
    }
437
438
}

439
static void RestartVideoToolbox(decoder_t *p_dec, bool b_reset_format)
440
441
442
443
444
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    msg_Dbg(p_dec, "Restarting decoder session");

445
    if (p_sys->session != nil)
446
        StopVideoToolbox(p_dec, b_reset_format);
447

448
    if (StartVideoToolbox(p_dec) != VLC_SUCCESS) {
449
450
451
452
        msg_Warn(p_dec, "Decoder session restart failed");
    }
}

453
454
#pragma mark - module open and close

455
456
457
458
459
460
461
static int SetupDecoderExtradata(decoder_t *p_dec)
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    CFMutableDictionaryRef extradata_info = NULL;

    if (p_sys->codec == kCMVideoCodecType_H264)
    {
462
463
464
465
466
467
468
        hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec),
                         p_dec->fmt_in.i_codec, true);
        int i_ret = hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in.p_extra,
                                          p_dec->fmt_in.i_extra);
        if (i_ret != VLC_SUCCESS)
            return i_ret;

469
470
471
472
473
474
475
476
        if (p_dec->fmt_in.p_extra)
        {
            int i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"),
                                            p_dec->fmt_in.p_extra,
                                            p_dec->fmt_in.i_extra);
            if (i_ret != VLC_SUCCESS)
                return i_ret;

477
478
479
            PicReorder_setup(p_dec);
        }
        /* else: AnnexB case, we'll get extradata from first input blocks */
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
    }
    else if (p_sys->codec == kCMVideoCodecType_MPEG4Video)
    {
        if (!p_dec->fmt_in.i_extra)
            return VLC_EGENERIC;
        int i_ret = ESDSCreate(p_dec, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra);
        if (i_ret != VLC_SUCCESS)
            return i_ret;
    }
    else
    {
        int i_ret = ExtradataInfoCreate(p_dec, NULL, NULL, 0);
        if (i_ret != VLC_SUCCESS)
            return i_ret;
    }

    return VLC_SUCCESS;
}

499
500
501
static int OpenDecoder(vlc_object_t *p_this)
{
    decoder_t *p_dec = (decoder_t *)p_this;
502
503
504
505
506
507
508

#if TARGET_OS_IPHONE
    if (unlikely([[UIDevice currentDevice].systemVersion floatValue] < 8.0)) {
        msg_Warn(p_dec, "decoder skipped as OS is too old");
        return VLC_EGENERIC;
    }
#endif
509

510
511
512
    if (p_dec->fmt_in.i_cat != VIDEO_ES)
        return VLC_EGENERIC;

513
514
515
516
    /* Fail if this module already failed to decode this ES */
    if (var_Type(p_dec, "videotoolbox-failed") != 0)
        return VLC_EGENERIC;

517
    /* check quickly if we can digest the offered data */
518
    CMVideoCodecType codec;
519
    codec = CodecPrecheck(p_dec);
520
    if (codec == -1)
521
522
523
524
525
526
527
528
529
        return VLC_EGENERIC;

    /* now that we see a chance to decode anything, allocate the
     * internals and start the decoding session */
    decoder_sys_t *p_sys;
    p_sys = malloc(sizeof(*p_sys));
    if (!p_sys)
        return VLC_ENOMEM;
    p_dec->p_sys = p_sys;
530
    p_sys->session = nil;
531
532
    p_sys->b_vt_feed = false;
    p_sys->b_vt_flush = false;
533
    p_sys->codec = codec;
534
    p_sys->videoFormatDescription = nil;
535
536
    p_sys->decoderConfiguration = nil;
    p_sys->destinationPixelBufferAttributes = nil;
537
    p_sys->extradataInfo = nil;
538
539
    p_sys->p_pic_reorder = NULL;
    p_sys->i_pic_reorder = 0;
540
    p_sys->i_pic_reorder_max = 1; /* 1 == no reordering */
541
    p_sys->b_format_propagated = false;
542
    p_sys->b_abort = false;
543
    p_sys->b_enable_temporal_processing = false;
544
    vlc_mutex_init(&p_sys->lock);
545

546
547
548
    /* return our proper VLC internal state */
    p_dec->fmt_out.i_cat = p_dec->fmt_in.i_cat;
    p_dec->fmt_out.video = p_dec->fmt_in.video;
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
    if (!p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den)
    {
        p_dec->fmt_out.video.i_sar_num = 1;
        p_dec->fmt_out.video.i_sar_den = 1;
    }
    if (!p_dec->fmt_out.video.i_visible_width
     || !p_dec->fmt_out.video.i_visible_height)
    {
        p_dec->fmt_out.video.i_visible_width = p_dec->fmt_out.video.i_width;
        p_dec->fmt_out.video.i_visible_height = p_dec->fmt_out.video.i_height;
    }
    else
    {
        p_dec->fmt_out.video.i_width = p_dec->fmt_out.video.i_visible_width;
        p_dec->fmt_out.video.i_height = p_dec->fmt_out.video.i_visible_height;
    }
565
566
567

    p_dec->fmt_out.i_codec = 0;

568
569
570
571
572
573
574
575
576
577
    int i_ret = SetupDecoderExtradata(p_dec);
    if (i_ret != VLC_SUCCESS)
        goto error;

    if (p_sys->extradataInfo != nil)
    {
        i_ret = StartVideoToolbox(p_dec);
        if (i_ret != VLC_SUCCESS)
            goto error;
    } /* else: late opening */
578

579
580
    p_dec->pf_decode = DecodeBlock;
    p_dec->pf_flush  = Flush;
581
582
583
584

    msg_Info(p_dec, "Using Video Toolbox to decode '%4.4s'", (char *)&p_dec->fmt_in.i_codec);

    return VLC_SUCCESS;
585
586
587
588

error:
    CloseDecoder(p_this);
    return i_ret;
589
590
591
592
593
594
595
}

static void CloseDecoder(vlc_object_t *p_this)
{
    decoder_t *p_dec = (decoder_t *)p_this;
    decoder_sys_t *p_sys = p_dec->p_sys;

596
    StopVideoToolbox(p_dec, true);
597

598
    vlc_mutex_destroy(&p_sys->lock);
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
    free(p_sys);
}

#pragma mark - helpers

static BOOL deviceSupportsAdvancedProfiles()
{
#if TARGET_IPHONE_SIMULATOR
    return NO;
#endif
#if TARGET_OS_IPHONE
    size_t size;
    cpu_type_t type;

    size = sizeof(type);
    sysctlbyname("hw.cputype", &type, &size, NULL, 0);

    /* Support for H264 profile HIGH 10 was introduced with the first 64bit Apple ARM SoC, the A7 */
    if (type == CPU_TYPE_ARM64)
        return YES;

    return NO;
#else
    return NO;
#endif
}

626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
static BOOL deviceSupportsAdvancedLevels()
{
#if TARGET_IPHONE_SIMULATOR
    return YES;
#endif
#if TARGET_OS_IPHONE
    size_t size;
    int32_t cpufamily;

    size = sizeof(cpufamily);
    sysctlbyname("hw.cpufamily", &cpufamily, &size, NULL, 0);

    /* Proper 4K decoding requires a Twister SoC
     * Everything below will kill the decoder daemon */
    if (cpufamily == CPUFAMILY_ARM_TWISTER) {
        return YES;
    }

    return NO;
#else
    return YES;
#endif
}

650
651
652
653
654
655
656
657
static inline void bo_add_mp4_tag_descr(bo_t *p_bo, uint8_t tag, uint32_t size)
{
    bo_add_8(p_bo, tag);
    for (int i = 3; i>0; i--)
        bo_add_8(p_bo, (size>>(7*i)) | 0x80);
    bo_add_8(p_bo, size & 0x7F);
}

658
static int ESDSCreate(decoder_t *p_dec, uint8_t *p_buf, uint32_t i_buf_size)
659
660
661
662
663
664
665
666
{
    int full_size = 3 + 5 +13 + 5 + i_buf_size + 3;
    int config_size = 13 + 5 + i_buf_size;
    int padding = 12;

    bo_t bo;
    bool status = bo_init(&bo, 1024);
    if (status != true)
667
        return VLC_EGENERIC;
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693

    bo_add_8(&bo, 0);       // Version
    bo_add_24be(&bo, 0);    // Flags

    // elementary stream description tag
    bo_add_mp4_tag_descr(&bo, 0x03, full_size);
    bo_add_16be(&bo, 0);    // esid
    bo_add_8(&bo, 0);       // stream priority (0-3)

    // decoder configuration description tag
    bo_add_mp4_tag_descr(&bo, 0x04, config_size);
    bo_add_8(&bo, 32);      // object type identification (32 == MPEG4)
    bo_add_8(&bo, 0x11);    // stream type
    bo_add_24be(&bo, 0);    // buffer size
    bo_add_32be(&bo, 0);    // max bitrate
    bo_add_32be(&bo, 0);    // avg bitrate

    // decoder specific description tag
    bo_add_mp4_tag_descr(&bo, 0x05, i_buf_size);
    bo_add_mem(&bo, i_buf_size, p_buf);

    // sync layer configuration description tag
    bo_add_8(&bo, 0x06);    // tag
    bo_add_8(&bo, 0x01);    // length
    bo_add_8(&bo, 0x02);    // no SL

694
695
    int i_ret = ExtradataInfoCreate(p_dec, CFSTR("esds"), bo.b->p_buffer,
                                    bo.b->i_buffer);
Thomas Guillem's avatar
Thomas Guillem committed
696
    bo_deinit(&bo);
697
698
699
    return i_ret;
}

700
static int avcCFromAnnexBCreate(decoder_t *p_dec)
701
702
703
{
    decoder_sys_t *p_sys = p_dec->p_sys;

704
    if (p_sys->hh.h264.i_sps_count == 0 || p_sys->hh.h264.i_pps_count == 0)
705
706
        return VLC_EGENERIC;

707
708
709
710
711
712
713
714
715
716
717
    unsigned i_h264_width, i_h264_height, i_video_width, i_video_height;
    int i_sar_num, i_sar_den, i_ret;
    i_ret = h264_helper_get_current_picture_size(&p_sys->hh,
                                                 &i_h264_width, &i_h264_height,
                                                 &i_video_width, &i_video_height);
    if (i_ret != VLC_SUCCESS)
        return i_ret;
    i_ret = h264_helper_get_current_sar(&p_sys->hh, &i_sar_num, &i_sar_den);
    if (i_ret != VLC_SUCCESS)
        return i_ret;
    PicReorder_setup(p_dec);
718
719
720
721
722

    p_dec->fmt_out.video.i_visible_width =
    p_dec->fmt_out.video.i_width = i_video_width;
    p_dec->fmt_out.video.i_visible_height =
    p_dec->fmt_out.video.i_height = i_video_height;
723
724
    p_dec->fmt_out.video.i_sar_num = i_sar_num;
    p_dec->fmt_out.video.i_sar_den = i_sar_den;
725

726
    block_t *p_avcC = h264_helper_get_avcc_config(&p_sys->hh);
727
728
729
    if (!p_avcC)
        return VLC_EGENERIC;

730
731
    i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"), p_avcC->p_buffer,
                                p_avcC->i_buffer);
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
    block_Release(p_avcC);
    return i_ret;
}

static int ExtradataInfoCreate(decoder_t *p_dec, CFStringRef name, void *p_data,
                               size_t i_data)
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    p_sys->extradataInfo =
        CFDictionaryCreateMutable(kCFAllocatorDefault, 1,
                                  &kCFTypeDictionaryKeyCallBacks,
                                  &kCFTypeDictionaryValueCallBacks);
    if (p_sys->extradataInfo == nil)
        return VLC_EGENERIC;

    if (p_data == NULL)
        return VLC_SUCCESS;

    CFDataRef extradata = CFDataCreate(kCFAllocatorDefault, p_data, i_data);
    if (extradata == nil)
    {
        CFRelease(p_sys->extradataInfo);
        p_sys->extradataInfo = nil;
        return VLC_EGENERIC;
    }
    CFDictionarySetValue(p_sys->extradataInfo, name, extradata);
    CFRelease(extradata);
    return VLC_SUCCESS;
761
762
763
764
}

static CMSampleBufferRef VTSampleBufferCreate(decoder_t *p_dec,
                                              CMFormatDescriptionRef fmt_desc,
Thomas Guillem's avatar
Thomas Guillem committed
765
                                              block_t *p_block)
766
767
768
769
770
{
    OSStatus status;
    CMBlockBufferRef  block_buf = NULL;
    CMSampleBufferRef sample_buf = NULL;

Thomas Guillem's avatar
Thomas Guillem committed
771
772
773
774
775
    CMSampleTimingInfo timeInfoArray[1] = { {
        .duration = CMTimeMake(p_block->i_length, 1),
        .presentationTimeStamp = CMTimeMake(p_block->i_pts > 0 ? p_block->i_pts : p_block->i_dts, CLOCK_FREQ),
        .decodeTimeStamp = CMTimeMake(p_block->i_dts, CLOCK_FREQ),
    } };
776
777

    status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,// structureAllocator
Thomas Guillem's avatar
Thomas Guillem committed
778
779
                                                p_block->p_buffer,  // memoryBlock
                                                p_block->i_buffer,  // blockLength
780
781
782
                                                kCFAllocatorNull,   // blockAllocator
                                                NULL,               // customBlockSource
                                                0,                  // offsetToData
Thomas Guillem's avatar
Thomas Guillem committed
783
                                                p_block->i_buffer,  // dataLength
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
                                                false,              // flags
                                                &block_buf);

    if (!status) {
        status = CMSampleBufferCreate(kCFAllocatorDefault,  // allocator
                                      block_buf,            // dataBuffer
                                      TRUE,                 // dataReady
                                      0,                    // makeDataReadyCallback
                                      0,                    // makeDataReadyRefcon
                                      fmt_desc,             // formatDescription
                                      1,                    // numSamples
                                      1,                    // numSampleTimingEntries
                                      timeInfoArray,        // sampleTimingArray
                                      0,                    // numSampleSizeEntries
                                      NULL,                 // sampleSizeArray
                                      &sample_buf);
        if (status != noErr)
801
            msg_Warn(p_dec, "sample buffer creation failure %i", (int)status);
802
    } else
803
        msg_Warn(p_dec, "cm block buffer creation failure %i", (int)status);
804

805
    if (block_buf != nil)
806
        CFRelease(block_buf);
807
    block_buf = nil;
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827

    return sample_buf;
}

void VTDictionarySetInt32(CFMutableDictionaryRef dict, CFStringRef key, int value)
{
    CFNumberRef number;
    number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
    CFDictionarySetValue(dict, key, number);
    CFRelease(number);
}

static void copy420YpCbCr8Planar(picture_t *p_pic,
                                 CVPixelBufferRef buffer,
                                 unsigned i_width,
                                 unsigned i_height)
{
    uint8_t *pp_plane[2];
    size_t pi_pitch[2];

828
    if (!buffer || i_width == 0 || i_height == 0)
829
830
        return;

831
    CVPixelBufferLockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
832
833
834
835
836
837

    for (int i = 0; i < 2; i++) {
        pp_plane[i] = CVPixelBufferGetBaseAddressOfPlane(buffer, i);
        pi_pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(buffer, i);
    }

838
    CopyFromNv12ToI420(p_pic, pp_plane, pi_pitch, i_height);
839

840
    CVPixelBufferUnlockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
841
842
}

843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
static int HandleVTStatus(decoder_t *p_dec, OSStatus status)
{
#define VTERRCASE(x) \
    case x: msg_Err(p_dec, "vt session error: '" #x "'"); break;

    switch (status)
    {
        case 0:
            return VLC_SUCCESS;

        VTERRCASE(kVTPropertyNotSupportedErr)
        VTERRCASE(kVTPropertyReadOnlyErr)
        VTERRCASE(kVTParameterErr)
        VTERRCASE(kVTInvalidSessionErr)
        VTERRCASE(kVTAllocationFailedErr)
        VTERRCASE(kVTPixelTransferNotSupportedErr)
        VTERRCASE(kVTCouldNotFindVideoDecoderErr)
        VTERRCASE(kVTCouldNotCreateInstanceErr)
        VTERRCASE(kVTCouldNotFindVideoEncoderErr)
        VTERRCASE(kVTVideoDecoderBadDataErr)
        VTERRCASE(kVTVideoDecoderUnsupportedDataFormatErr)
        VTERRCASE(kVTVideoDecoderMalfunctionErr)
        VTERRCASE(kVTVideoEncoderMalfunctionErr)
        VTERRCASE(kVTVideoDecoderNotAvailableNowErr)
        VTERRCASE(kVTImageRotationNotSupportedErr)
        VTERRCASE(kVTVideoEncoderNotAvailableNowErr)
        VTERRCASE(kVTFormatDescriptionChangeNotSupportedErr)
        VTERRCASE(kVTInsufficientSourceColorDataErr)
        VTERRCASE(kVTCouldNotCreateColorCorrectionDataErr)
        VTERRCASE(kVTColorSyncTransformConvertFailedErr)
        VTERRCASE(kVTVideoDecoderAuthorizationErr)
        VTERRCASE(kVTVideoEncoderAuthorizationErr)
        VTERRCASE(kVTColorCorrectionPixelTransferFailedErr)
        VTERRCASE(kVTMultiPassStorageIdentifierMismatchErr)
        VTERRCASE(kVTMultiPassStorageInvalidErr)
        VTERRCASE(kVTFrameSiloInvalidTimeStampErr)
        VTERRCASE(kVTFrameSiloInvalidTimeRangeErr)
        VTERRCASE(kVTCouldNotFindTemporalFilterErr)
        VTERRCASE(kVTPixelTransferNotPermittedErr)
        VTERRCASE(kVTColorCorrectionImageRotationFailedErr)

        default:
            msg_Err(p_dec, "unknown vt session error (%i)", (int)status);
    }
#undef VTERRCASE
    return VLC_EGENERIC;
}

891
892
#pragma mark - actual decoding

893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
static void PicReorder_pushSorted(decoder_t *p_dec, picture_t *p_pic)
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    assert(p_pic->p_next == NULL);
    p_sys->i_pic_reorder++;
    if (p_sys->p_pic_reorder == NULL)
    {
        p_sys->p_pic_reorder = p_pic;
        return;
    }

    picture_t **pp_last = &p_sys->p_pic_reorder;
    for (picture_t *p_cur = *pp_last; p_cur != NULL;
         pp_last = &p_cur->p_next, p_cur = *pp_last)
    {
        if (p_pic->date < p_cur->date)
        {
            p_pic->p_next = p_cur;
            *pp_last = p_pic;
            return;
        }
    }
    *pp_last = p_pic;
}

static picture_t *PicReorder_pop(decoder_t *p_dec, bool force)
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    if (p_sys->i_pic_reorder == 0
924
     || (!force && p_sys->i_pic_reorder < p_sys->i_pic_reorder_max))
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
        return NULL;

    picture_t *p_pic = p_sys->p_pic_reorder;
    p_sys->p_pic_reorder = p_pic->p_next;
    p_pic->p_next = NULL;
    p_sys->i_pic_reorder--;
    return p_pic;
}

static void PicReorder_flush(decoder_t *p_dec)
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    for (picture_t *p_cur = p_sys->p_pic_reorder, *p_next;
         p_cur != NULL; p_cur = p_next)
    {
        p_next = p_cur->p_next;
        picture_Release(p_cur);
    }
    p_sys->i_pic_reorder = 0;
    p_sys->p_pic_reorder = NULL;
}

948
949
950
951
952
953
954
955
956
957
958
static void PicReorder_setup(decoder_t *p_dec)
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    uint8_t i_depth;
    unsigned i_delay;
    if (h264_helper_get_current_dpb_values(&p_sys->hh, &i_depth, &i_delay)
     != VLC_SUCCESS)
        i_depth = 4;
    p_sys->i_pic_reorder_max = i_depth + 1;
}

959
960
961
962
static void Flush(decoder_t *p_dec)
{
    decoder_sys_t *p_sys = p_dec->p_sys;

963
964
965
966
    /* There is no Flush in VT api, ask to restart VT from next DecodeBlock if
     * we already feed some input blocks (it's better to not restart here in
     * order to avoid useless restart just before a close). */
    p_sys->b_vt_flush = p_sys->b_vt_feed;
967
968
}

969
static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
970
971
972
{
    decoder_sys_t *p_sys = p_dec->p_sys;

973
974
975
976
977
    if (p_sys->b_vt_flush) {
        RestartVideoToolbox(p_dec, false);
        p_sys->b_vt_flush = false;
    }

978
    if (!p_block)
Thomas Guillem's avatar
Thomas Guillem committed
979
980
981
982
    {
        /* draining: return last pictures of the reordered queue */
        if (p_sys->session)
            VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
983
984
985
986
987
        for (;;)
        {
            vlc_mutex_lock(&p_sys->lock);
            picture_t *p_pic = PicReorder_pop(p_dec, true);
            vlc_mutex_unlock(&p_sys->lock);
Thomas Guillem's avatar
Thomas Guillem committed
988

989
990
991
992
993
994
995
            if (p_pic)
                decoder_QueueVideo(p_dec, p_pic);
            else
                break;
        }
        return VLCDEC_SUCCESS;
    }
996

997
998
999
1000
1001
1002
1003
    vlc_mutex_lock(&p_sys->lock);
    if (p_sys->b_abort) { /* abort from output thread (DecoderCallback) */
        vlc_mutex_unlock(&p_sys->lock);
        goto reload;
    }
    vlc_mutex_unlock(&p_sys->lock);

Thomas Guillem's avatar
Thomas Guillem committed
1004
1005
1006
1007
1008
1009
1010
    if (unlikely(p_block->i_flags&(BLOCK_FLAG_CORRUPTED)))
    {
        if (p_sys->b_vt_feed)
            RestartVideoToolbox(p_dec, false);
        goto skip;
    }

1011
1012
1013
1014
1015
1016
1017
1018
1019
    bool b_config_changed = false;
    if (p_sys->codec == kCMVideoCodecType_H264 && p_sys->hh.pf_process_block)
    {
        p_block = p_sys->hh.pf_process_block(&p_sys->hh, p_block, &b_config_changed);
        if (!p_block)
            return VLCDEC_SUCCESS;
    }

    if (b_config_changed)
1020
    {
Thomas Guillem's avatar
Thomas Guillem committed
1021
1022
        /* decoding didn't start yet, which is ok for H264, let's see
         * if we can use this block to get going */
1023
1024
1025
1026
1027
1028
        assert(p_sys->codec == kCMVideoCodecType_H264 && !p_sys->hh.b_is_xvcC);
        if (p_sys->session)
        {
            msg_Dbg(p_dec, "SPS/PPS changed: restarting H264 decoder");
            StopVideoToolbox(p_dec, true);
        }
1029

1030
        int i_ret = avcCFromAnnexBCreate(p_dec);
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
        if (i_ret == VLC_SUCCESS)
        {
            if ((p_block->i_flags & BLOCK_FLAG_TOP_FIELD_FIRST
             || p_block->i_flags & BLOCK_FLAG_BOTTOM_FIELD_FIRST)
             && var_InheritBool(p_dec, "videotoolbox-temporal-deinterlacing"))
                p_sys->b_enable_temporal_processing = true;

            msg_Dbg(p_dec, "Got SPS/PPS: late opening of H264 decoder");
            StartVideoToolbox(p_dec);
        }
        if (!p_sys->session)
            goto skip;
Thomas Guillem's avatar
Thomas Guillem committed
1043
    }
1044

1045
1046
1047
1048
1049
1050
1051
1052
1053
    if (p_block->i_pts == VLC_TS_INVALID && p_block->i_dts != VLC_TS_INVALID &&
        p_sys->i_pic_reorder_max > 1)
    {
        /* VideoToolbox won't reorder output frames and there is no way to know
         * the right order. Abort and use an other decoder. */
        msg_Warn(p_dec, "unable to reorder output frames, abort");
        goto reload;
    }

Thomas Guillem's avatar
Thomas Guillem committed
1054
1055
1056
1057
    CMSampleBufferRef sampleBuffer =
        VTSampleBufferCreate(p_dec, p_sys->videoFormatDescription, p_block);
    if (unlikely(!sampleBuffer))
        goto skip;
1058

Thomas Guillem's avatar
Thomas Guillem committed
1059
1060
1061
1062
1063
1064
1065
1066
1067
    VTDecodeInfoFlags flagOut;
    VTDecodeFrameFlags decoderFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
    if (unlikely(p_sys->b_enable_temporal_processing))
        decoderFlags |= kVTDecodeFrame_EnableTemporalProcessing;

    OSStatus status =
        VTDecompressionSessionDecodeFrame(p_sys->session, sampleBuffer,
                                          decoderFlags, NULL, &flagOut);
    CFRelease(sampleBuffer);
1068
    if (HandleVTStatus(p_dec, status) == VLC_SUCCESS)
Thomas Guillem's avatar
Thomas Guillem committed
1069
        p_sys->b_vt_feed = true;
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
    else
    {
        switch (status)
        {
            case kCVReturnInvalidArgument:
                msg_Err(p_dec, "decoder failure: invalid argument");
                /* The decoder module will be reloaded next time since we already
                 * modified the input block */
                vlc_mutex_lock(&p_sys->lock);
                p_dec->p_sys->b_abort = true;
                vlc_mutex_unlock(&p_sys->lock);
                break;
            case -8969 /* codecBadDataErr */:
            case kVTVideoDecoderBadDataErr:
                StopVideoToolbox(p_dec, true);
                break;
            case -8960 /* codecErr */:
            case kVTVideoDecoderMalfunctionErr:
            case kVTInvalidSessionErr:
                RestartVideoToolbox(p_dec, true);
                break;
        }
1092
1093
1094
    }

skip:
Thomas Guillem's avatar
Thomas Guillem committed
1095
    block_Release(p_block);
1096
    return VLCDEC_SUCCESS;
1097
1098
1099
1100

reload:
    /* Add an empty variable so that videotoolbox won't be loaded again for
     * this ES */
1101
1102
    var_Create(p_dec, "videotoolbox-failed", VLC_VAR_VOID);
    return VLCDEC_RELOAD;
1103
1104
}

1105
static int UpdateVideoFormat(decoder_t *p_dec, CVPixelBufferRef imageBuffer)
1106
{
1107
1108
1109
1110
1111
1112
1113
    CFDictionaryRef attachments = CVBufferGetAttachments(imageBuffer, kCVAttachmentMode_ShouldPropagate);
    NSDictionary *attachmentDict = (NSDictionary *)attachments;
#ifndef NDEBUG
    NSLog(@"%@", attachments);
#endif
    if (attachmentDict == nil || attachmentDict.count == 0)
        return -1;
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169

    NSString *colorSpace = attachmentDict[(NSString *)kCVImageBufferYCbCrMatrixKey];
    if (colorSpace != nil) {
        if ([colorSpace isEqualToString:(NSString *)kCVImageBufferYCbCrMatrix_ITU_R_601_4])
            p_dec->fmt_out.video.space = COLOR_SPACE_BT601;
        else if ([colorSpace isEqualToString:(NSString *)kCVImageBufferYCbCrMatrix_ITU_R_709_2])
            p_dec->fmt_out.video.space = COLOR_SPACE_BT709;
        else
            p_dec->fmt_out.video.space = COLOR_SPACE_UNDEF;
    }

    NSString *colorprimary = attachmentDict[(NSString *)kCVImageBufferColorPrimariesKey];
    if (colorprimary != nil) {
        if ([colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_SMPTE_C] ||
            [colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_EBU_3213])
            p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT601_625;
        else if ([colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_ITU_R_709_2])
            p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_BT709;
        else if ([colorprimary isEqualToString:(NSString *)kCVImageBufferColorPrimaries_P22])
            p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_DCI_P3;
        else
            p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_UNDEF;
    }

    NSString *transfer = attachmentDict[(NSString *)kCVImageBufferTransferFunctionKey];
    if (transfer != nil) {
        if ([transfer isEqualToString:(NSString *)kCVImageBufferTransferFunction_ITU_R_709_2] ||
            [transfer isEqualToString:(NSString *)kCVImageBufferTransferFunction_SMPTE_240M_1995])
            p_dec->fmt_out.video.transfer = TRANSFER_FUNC_BT709;
        else
            p_dec->fmt_out.video.transfer = TRANSFER_FUNC_UNDEF;
    }

    NSString *chromaLocation = attachmentDict[(NSString *)kCVImageBufferChromaLocationTopFieldKey];
    if (chromaLocation != nil) {
        if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Left] ||
            [chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_DV420])
            p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_LEFT;
        else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Center])
            p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_CENTER;
        else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_TopLeft])
            p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_TOP_LEFT;
        else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Top])
            p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_TOP_CENTER;
        else
            p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_UNDEF;
    }
    if (p_dec->fmt_out.video.chroma_location == CHROMA_LOCATION_UNDEF) {
        chromaLocation = attachmentDict[(NSString *)kCVImageBufferChromaLocationBottomFieldKey];
        if (chromaLocation != nil) {
            if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_BottomLeft])
                p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_BOTTOM_LEFT;
            else if ([chromaLocation isEqualToString:(NSString *)kCVImageBufferChromaLocation_Bottom])
                p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_BOTTOM_CENTER;
        }
    }
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192

    uint32_t cvfmt = CVPixelBufferGetPixelFormatType(imageBuffer);
    switch (cvfmt)
    {
        case kCVPixelFormatType_422YpCbCr8:
        case 'yuv2':
            p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_UYVY;
            assert(CVPixelBufferIsPlanar(imageBuffer) == false);
            break;
        case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
        case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
            p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_NV12;
            assert(CVPixelBufferIsPlanar(imageBuffer) == true);
            break;
        case kCVPixelFormatType_420YpCbCr8Planar:
            p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_I420;
            assert(CVPixelBufferIsPlanar(imageBuffer) == true);
            break;
        case kCVPixelFormatType_32BGRA:
            p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_BGRA;
            assert(CVPixelBufferIsPlanar(imageBuffer) == false);
            break;
        default:
1193
            p_dec->p_sys->b_abort = true;
1194
1195
1196
            return -1;
    }
    return decoder_UpdateVideoFormat(p_dec);
1197
1198
}

1199
static void DecoderCallback(void *decompressionOutputRefCon,
1200
1201
1202
1203
1204
1205
                            void *sourceFrameRefCon,
                            OSStatus status,
                            VTDecodeInfoFlags infoFlags,
                            CVPixelBufferRef imageBuffer,
                            CMTime pts,
                            CMTime duration)
1206
1207
1208
1209
1210
1211
1212
{
    VLC_UNUSED(sourceFrameRefCon);
    VLC_UNUSED(duration);
    decoder_t *p_dec = (decoder_t *)decompressionOutputRefCon;
    decoder_sys_t *p_sys = p_dec->p_sys;

    if (status != noErr) {
1213
        msg_Warn(p_dec, "decoding of a frame failed (%i, %u)", (int)status, (unsigned int) infoFlags);
1214
1215
        return;
    }
1216
    assert(imageBuffer);
1217

1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
    if (unlikely(!p_sys->b_format_propagated)) {
        vlc_mutex_lock(&p_sys->lock);
        p_sys->b_format_propagated =
            UpdateVideoFormat(p_dec, imageBuffer) == VLC_SUCCESS;
        vlc_mutex_unlock(&p_sys->lock);

        if (!p_sys->b_format_propagated)
            return;
        assert(p_dec->fmt_out.i_codec != 0);
    }

1229
1230
    if (infoFlags & kVTDecodeInfo_FrameDropped)
    {
1231
1232
1233
1234
        msg_Dbg(p_dec, "decoder dropped frame");
        return;
    }

1235
    if (!CMTIME_IS_VALID(pts))
1236
        return;
1237

1238
    if (CVPixelBufferGetDataSize(imageBuffer) == 0)
1239
1240
        return;

1241
1242
1243
    picture_t *p_pic = decoder_NewPicture(p_dec);
    if (!p_pic)
        return;
1244
    if (!p_pic->p_sys) {
1245
1246
1247
        vlc_mutex_lock(&p_sys->lock);
        p_dec->p_sys->b_abort = true;
        vlc_mutex_unlock(&p_sys->lock);
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
        picture_Release(p_pic);
        return;
    }

    /* Can happen if the pic was discarded */
    if (p_pic->p_sys->pixelBuffer != nil)
        CFRelease(p_pic->p_sys->pixelBuffer);

    /* will be freed by the vout */
    p_pic->p_sys->pixelBuffer = CVPixelBufferRetain(imageBuffer);
1258

1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
    p_pic->date = pts.value;
    p_pic->b_progressive = true;

    vlc_mutex_lock(&p_sys->lock);
    PicReorder_pushSorted(p_dec, p_pic);
    p_pic = PicReorder_pop(p_dec, false);
    vlc_mutex_unlock(&p_sys->lock);

    if (p_pic != NULL)
        decoder_QueueVideo(p_dec, p_pic);
    return;
1270
}