1 /*****************************************************************************
2 * videotoolbox.m: Video Toolbox decoder
3 *****************************************************************************
4 * Copyright © 2014-2015 VideoLabs SAS
6 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
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.
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.
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 *****************************************************************************/
29 #import <vlc_common.h>
30 #import <vlc_plugin.h>
32 #import "hxxx_helper.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>
48 #import <sys/sysctl.h>
49 #import <mach/machine.h>
52 #import <UIKit/UIKit.h>
54 /* support iOS SDKs < v9.1 */
55 #ifndef CPUFAMILY_ARM_TWISTER
56 #define CPUFAMILY_ARM_TWISTER 0x92fb37c8
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");
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.")
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)
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;
110 picture_t *p_picture;
116 bool b_top_field_first;
119 frame_info_t *p_next;
122 #pragma mark - decoder structure
124 #define H264_MAX_DPB 16
128 CMVideoCodecType codec;
129 struct hxxx_helper hh;
133 VTDecompressionSessionRef session;
134 CMVideoFormatDescriptionRef videoFormatDescription;
135 CFMutableDictionaryRef decoderConfiguration;
136 CFMutableDictionaryRef destinationPixelBufferAttributes;
137 CFMutableDictionaryRef extradataInfo;
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;
150 poc_context_t pocctx;
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;
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)
180 struct sei_callback_s *s = priv;
181 if(s->p_sps && s->p_sps->vui.b_valid)
183 if(s->p_sps->vui.b_hrd_parameters_present_flag)
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);
189 if(s->p_sps->vui.b_pic_struct_present_flag)
190 s->i_pic_struct = bs_read( p_sei_data->p_bs, 4);
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))
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)
218 if(!h264_decode_slice(p_nal, i_nal, GetSPSPPS, p_sys, &slice))
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);
226 if(!p_sys->b_invalid_pic_reorder_max && i_nal_type == H264_NAL_SLICE_IDR)
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);
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;
247 sei.i_pic_struct = UINT8_MAX;
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 )
263 date_Change( &p_sys->pts, p_sps->vui.i_time_scale,
264 p_sps->vui.i_num_units_in_tick );
268 return true; /* No need to parse further NAL */
270 else if(i_nal_type == H264_NAL_SEI)
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))
287 if(*pp_lead_in == NULL)
289 else if(p_sys->b_poc_based_reorder)
290 b_insert = ((*pp_lead_in)->i_foc > p_info->i_foc);
292 b_insert = ((*pp_lead_in)->p_picture->date >= p_info->p_picture->date);
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;
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);
309 static picture_t * RemoveOneFrameFromDPB(decoder_sys_t *p_sys)
311 frame_info_t *p_info = p_sys->p_pic_reorder;
315 const int i_framepoc = p_info->i_poc;
317 picture_t *p_ret = NULL;
318 picture_t **pp_ret_last = &p_ret;
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);
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);
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;
344 p_info = p_sys->p_pic_reorder;
348 if (p_sys->b_poc_based_reorder)
349 b_dequeue = (p_info->i_poc == i_framepoc);
351 b_dequeue = (p_field->date == p_info->p_picture->date);
353 else b_dequeue = false;
360 static void DrainDPB(decoder_t *p_dec)
362 decoder_sys_t *p_sys = p_dec->p_sys;
365 picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
366 if (p_fields == NULL)
370 picture_t *p_next = p_fields->p_next;
371 p_fields->p_next = NULL;
372 decoder_QueueVideo(p_dec, p_fields);
374 } while(p_fields != NULL);
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));
385 if (p_sys->b_poc_based_reorder)
387 if(p_sys->codec != kCMVideoCodecType_H264 ||
388 !ParseH264NAL(p_dec, p_block->p_buffer, p_block->i_buffer, 4, p_info))
390 assert(p_sys->codec == kCMVideoCodecType_H264);
397 p_info->i_num_ts = 2;
398 p_info->b_progressive = true;
399 p_info->b_field = false;
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);
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))
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)
420 if(p_sys->b_poc_based_reorder && p_sys->p_pic_reorder->i_foc > p_info->i_foc)
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);
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)
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);
438 picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
439 if (p_fields == NULL)
443 picture_t *p_next = p_fields->p_next;
444 p_fields->p_next = NULL;
445 decoder_QueueVideo(p_dec, p_fields);
447 } while(p_fields != NULL);
450 InsertIntoDPB(p_sys, p_info);
453 static CMVideoCodecType CodecPrecheck(decoder_t *p_dec)
455 uint8_t i_profile = 0xFF, i_level = 0xFF;
457 CMVideoCodecType codec;
459 /* check for the codec we can and want to decode */
460 switch (p_dec->fmt_in.i_codec) {
462 codec = kCMVideoCodecType_H264;
464 b_ret = h264_get_profile_level(&p_dec->fmt_in, &i_profile, &i_level, NULL);
466 msg_Warn(p_dec, "H264 profile and level parsing failed because it didn't arrive yet");
467 return kCMVideoCodecType_H264;
470 msg_Dbg(p_dec, "trying to decode MPEG-4 Part 10: profile %" PRIx8 ", level %" PRIx8, i_profile, i_level);
473 case PROFILE_H264_BASELINE:
474 case PROFILE_H264_MAIN:
475 case PROFILE_H264_HIGH:
478 case PROFILE_H264_HIGH_10:
480 if (deviceSupportsAdvancedProfiles())
486 msg_Dbg(p_dec, "unsupported H264 profile %" PRIx8, i_profile);
491 #if !TARGET_OS_IPHONE
492 /* a level higher than 5.2 was not tested, so don't dare to
495 msg_Dbg(p_dec, "unsupported H264 level %" PRIx8, i_level);
499 /* on SoC A8, 4.2 is the highest specified profile */
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);
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");
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;
522 #if !TARGET_OS_IPHONE
524 codec = kCMVideoCodecType_H263;
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;
536 case VLC_FOURCC( 'a','p','c','h' ):
537 codec = kCMVideoCodecType_AppleProRes422HQ;
540 case VLC_FOURCC( 'a','p','c','s' ):
541 codec = kCMVideoCodecType_AppleProRes422LT;
544 case VLC_FOURCC( 'a','p','c','o' ):
545 codec = kCMVideoCodecType_AppleProRes422Proxy;
549 codec = kCMVideoCodecType_AppleProRes422;
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;
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;
577 /* mpgv / mp2v needs fixing, so disable it for now */
580 codec = kCMVideoCodecType_MPEG1Video;
583 codec = kCMVideoCodecType_MPEG2Video;
589 msg_Err(p_dec, "'%4.4s' is not supported", (char *)&p_dec->fmt_in.i_codec);
597 static int StartVideoToolbox(decoder_t *p_dec)
599 decoder_sys_t *p_sys = p_dec->p_sys;
602 assert(p_sys->extradataInfo != nil);
604 p_sys->decoderConfiguration =
605 CFDictionaryCreateMutable(kCFAllocatorDefault, 2,
606 &kCFTypeDictionaryKeyCallBacks,
607 &kCFTypeDictionaryValueCallBacks);
608 if (p_sys->decoderConfiguration == NULL)
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 )
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 );
638 else date_Init( &p_sys->pts, 2 * 30000, 1001 );
640 VTDictionarySetInt32(pixelaspectratio,
641 kCVImageBufferPixelAspectRatioHorizontalSpacingKey,
643 VTDictionarySetInt32(pixelaspectratio,
644 kCVImageBufferPixelAspectRatioVerticalSpacingKey,
646 CFDictionarySetValue(p_sys->decoderConfiguration,
647 kCVImageBufferPixelAspectRatioKey,
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,
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,
665 if (p_sys->b_enable_temporal_processing)
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);
676 /* create video format description */
677 status = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
681 p_sys->decoderConfiguration,
682 &p_sys->videoFormatDescription);
684 CFRelease(p_sys->decoderConfiguration);
685 msg_Err(p_dec, "video format description creation failed (%i)", (int)status);
689 /* destination pixel buffer attributes */
690 p_sys->destinationPixelBufferAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault,
692 &kCFTypeDictionaryKeyCallBacks,
693 &kCFTypeDictionaryValueCallBacks);
695 #if !TARGET_OS_IPHONE
696 CFDictionarySetValue(p_sys->destinationPixelBufferAttributes,
697 kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey,
700 CFDictionarySetValue(p_sys->destinationPixelBufferAttributes,
701 kCVPixelBufferOpenGLESCompatibilityKey,
705 VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
706 kCVPixelBufferWidthKey,
708 VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
709 kCVPixelBufferHeightKey,
711 VTDictionarySetInt32(p_sys->destinationPixelBufferAttributes,
712 kCVPixelBufferBytesPerRowAlignmentKey,
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)
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)
739 VTDecompressionSessionInvalidate(p_sys->session);
740 CFRelease(p_sys->session);
741 p_sys->session = nil;
745 p_sys->b_format_propagated = false;
746 p_dec->fmt_out.i_codec = 0;
751 if (p_sys->videoFormatDescription != nil) {
752 CFRelease(p_sys->videoFormatDescription);
753 p_sys->videoFormatDescription = nil;
755 if (p_sys->decoderConfiguration != nil) {
756 CFRelease(p_sys->decoderConfiguration);
757 p_sys->decoderConfiguration = nil;
759 if (p_sys->destinationPixelBufferAttributes != nil) {
760 CFRelease(p_sys->destinationPixelBufferAttributes);
761 p_sys->destinationPixelBufferAttributes = nil;
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)
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)
792 assert(p_sys->hh.pf_process_block != NULL);
794 if (p_dec->fmt_in.p_extra)
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)
802 /* else: AnnexB case, we'll get extradata from first input blocks */
804 else if (p_sys->codec == kCMVideoCodecType_MPEG4Video)
806 if (!p_dec->fmt_in.i_extra)
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)
814 int i_ret = ExtradataInfoCreate(p_dec, NULL, NULL, 0);
815 if (i_ret != VLC_SUCCESS)
822 static int OpenDecoder(vlc_object_t *p_this)
824 decoder_t *p_dec = (decoder_t *)p_this;
827 if (unlikely([[UIDevice currentDevice].systemVersion floatValue] < 8.0)) {
828 msg_Warn(p_dec, "decoder skipped as OS is too old");
833 if (p_dec->fmt_in.i_cat != VIDEO_ES)
836 /* Fail if this module already failed to decode this ES */
837 if (var_Type(p_dec, "videotoolbox-failed") != 0)
840 /* check quickly if we can digest the offered data */
841 CMVideoCodecType codec;
842 codec = CodecPrecheck(p_dec);
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));
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)
877 p_dec->fmt_out.video.i_sar_num = 1;
878 p_dec->fmt_out.video.i_sar_den = 1;
880 if (!p_dec->fmt_out.video.i_visible_width
881 || !p_dec->fmt_out.video.i_visible_height)
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;
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;
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)
901 if (p_sys->extradataInfo != nil)
903 i_ret = StartVideoToolbox(p_dec);
904 if (i_ret != VLC_SUCCESS)
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);
916 CloseDecoder(p_this);
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);
934 #pragma mark - helpers
936 static BOOL deviceSupportsAdvancedProfiles()
938 #if TARGET_IPHONE_SIMULATOR
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)
958 static BOOL deviceSupportsAdvancedLevels()
960 #if TARGET_IPHONE_SIMULATOR
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) {
982 static inline void bo_add_mp4_tag_descr(bo_t *p_bo, uint8_t tag, uint32_t size)
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;
997 bool status = bo_init(&bo, 1024);
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,
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)
1046 i_ret = h264_helper_get_current_sar(&p_sys->hh, &i_sar_num, &i_sar_den);
1047 if (i_ret != VLC_SUCCESS)
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);
1059 return VLC_EGENERIC;
1061 i_ret = ExtradataInfoCreate(p_dec, CFSTR("avcC"), p_avcC->p_buffer,
1063 block_Release(p_avcC);
1067 static int ExtradataInfoCreate(decoder_t *p_dec, CFStringRef name, void *p_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;
1082 CFDataRef extradata = CFDataCreate(kCFAllocatorDefault, p_data, i_data);
1083 if (extradata == nil)
1085 CFRelease(p_sys->extradataInfo);
1086 p_sys->extradataInfo = nil;
1087 return VLC_EGENERIC;
1089 CFDictionarySetValue(p_sys->extradataInfo, name, extradata);
1090 CFRelease(extradata);
1094 static CMSampleBufferRef VTSampleBufferCreate(decoder_t *p_dec,
1095 CMFormatDescriptionRef fmt_desc,
1099 CMBlockBufferRef block_buf = NULL;
1100 CMSampleBufferRef sample_buf = NULL;
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);
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),
1113 status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,// structureAllocator
1114 p_block->p_buffer, // memoryBlock
1115 p_block->i_buffer, // blockLength
1116 kCFAllocatorNull, // blockAllocator
1117 NULL, // customBlockSource
1119 p_block->i_buffer, // dataLength
1124 status = CMSampleBufferCreate(kCFAllocatorDefault, // allocator
1125 block_buf, // dataBuffer
1127 0, // makeDataReadyCallback
1128 0, // makeDataReadyRefcon
1129 fmt_desc, // formatDescription
1131 1, // numSampleTimingEntries
1132 timeInfoArray, // sampleTimingArray
1133 0, // numSampleSizeEntries
1134 NULL, // sampleSizeArray
1136 if (status != noErr)
1137 msg_Warn(p_dec, "sample buffer creation failure %i", (int)status);
1139 msg_Warn(p_dec, "cm block buffer creation failure %i", (int)status);
1141 if (block_buf != nil)
1142 CFRelease(block_buf);
1148 void VTDictionarySetInt32(CFMutableDictionaryRef dict, CFStringRef key, int value)
1151 number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
1152 CFDictionarySetValue(dict, key, number);
1156 static void copy420YpCbCr8Planar(picture_t *p_pic,
1157 CVPixelBufferRef buffer,
1161 uint8_t *pp_plane[2];
1164 if (!buffer || i_width == 0 || i_height == 0)
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);
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;
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)
1219 msg_Err(p_dec, "vt session error: "
1220 "'kVTColorCorrectionImageRotationFailedErr'");
1223 msg_Err(p_dec, "unknown vt session error (%i)", (int)status);
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 */
1247 VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
1249 vlc_mutex_lock(&p_sys->lock);
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;
1263 if (p_block == NULL)
1266 return VLCDEC_SUCCESS;
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
1274 var_Create(p_dec, "videotoolbox-failed", VLC_VAR_VOID);
1275 return VLCDEC_RELOAD;
1277 vlc_mutex_unlock(&p_sys->lock);
1279 if (unlikely(p_block->i_flags&(BLOCK_FLAG_CORRUPTED)))
1281 if (p_sys->b_vt_feed)
1284 RestartVideoToolbox(p_dec, false);
1289 bool b_config_changed = false;
1290 if (p_sys->codec == kCMVideoCodecType_H264)
1292 p_block = p_sys->hh.pf_process_block(&p_sys->hh, p_block, &b_config_changed);
1294 return VLCDEC_SUCCESS;
1297 if (b_config_changed)
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);
1304 msg_Dbg(p_dec, "SPS/PPS changed: draining H264 decoder");
1306 msg_Dbg(p_dec, "SPS/PPS changed: restarting H264 decoder");
1307 StopVideoToolbox(p_dec, true);
1310 int i_ret = avcCFromAnnexBCreate(p_dec);
1311 if (i_ret == VLC_SUCCESS)
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);
1320 if (!p_sys->session)
1324 frame_info_t *p_info = CreateReorderInfo(p_dec, p_block);
1325 if(unlikely(!p_info))
1328 CMSampleBufferRef sampleBuffer =
1329 VTSampleBufferCreate(p_dec, p_sys->videoFormatDescription, p_block);
1330 if (unlikely(!sampleBuffer))
1336 VTDecodeInfoFlags flagOut;
1337 VTDecodeFrameFlags decoderFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
1338 if (unlikely(p_sys->b_enable_temporal_processing))
1339 decoderFlags |= kVTDecodeFrame_EnableTemporalProcessing;
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;
1348 bool b_abort = false;
1351 case -8960 /* codecErr */:
1352 case kCVReturnInvalidArgument:
1353 case kVTVideoDecoderMalfunctionErr:
1356 case -8969 /* codecBadDataErr */:
1357 case kVTVideoDecoderBadDataErr:
1358 if (RestartVideoToolbox(p_dec, true) == VLC_SUCCESS)
1360 status = VTDecompressionSessionDecodeFrame(p_sys->session,
1361 sampleBuffer, decoderFlags, p_info, &flagOut);
1370 case kVTInvalidSessionErr:
1371 RestartVideoToolbox(p_dec, true);
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);
1384 CFRelease(sampleBuffer);
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;
1396 NSLog(@"%@", attachments);
1398 if (attachmentDict == nil || attachmentDict.count == 0)
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;
1408 p_dec->fmt_out.video.space = COLOR_SPACE_UNDEF;
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;
1421 p_dec->fmt_out.video.primaries = COLOR_PRIMARIES_UNDEF;
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;
1430 p_dec->fmt_out.video.transfer = TRANSFER_FUNC_UNDEF;
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;
1445 p_dec->fmt_out.video.chroma_location = CHROMA_LOCATION_UNDEF;
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;
1457 uint32_t cvfmt = CVPixelBufferGetPixelFormatType(imageBuffer);
1460 case kCVPixelFormatType_422YpCbCr8:
1462 p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_UYVY;
1463 assert(CVPixelBufferIsPlanar(imageBuffer) == false);
1465 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
1466 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
1467 p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_NV12;
1468 assert(CVPixelBufferIsPlanar(imageBuffer) == true);
1470 case kCVPixelFormatType_420YpCbCr8Planar:
1471 p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_I420;
1472 assert(CVPixelBufferIsPlanar(imageBuffer) == true);
1474 case kCVPixelFormatType_32BGRA:
1475 p_dec->fmt_out.i_codec = VLC_CODEC_CVPX_BGRA;
1476 assert(CVPixelBufferIsPlanar(imageBuffer) == false);
1479 p_dec->p_sys->b_abort = true;
1482 return decoder_UpdateVideoFormat(p_dec);
1485 static void DecoderCallback(void *decompressionOutputRefCon,
1486 void *sourceFrameRefCon,
1488 VTDecodeInfoFlags infoFlags,
1489 CVPixelBufferRef imageBuffer,
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 )
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)
1517 assert(p_dec->fmt_out.i_codec != 0);
1520 if (infoFlags & kVTDecodeInfo_FrameDropped)
1522 /* We can't trust VT, some decoded frames can be marked as dropped */
1523 msg_Dbg(p_dec, "decoder dropped frame");
1526 if (!CMTIME_IS_VALID(pts))
1532 if (CVPixelBufferGetDataSize(imageBuffer) == 0)
1540 picture_t *p_pic = decoder_NewPicture(p_dec);
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);
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)
1569 p_pic->i_nb_fields = p_info->i_num_ts;
1570 p_pic->b_top_field_first = p_info->b_top_field_first;
1573 vlc_mutex_lock(&p_sys->lock);
1574 OnDecodedFrame( p_dec, p_info );
1575 vlc_mutex_unlock(&p_sys->lock);