[vlc-commits] codec: videotoolbox: add HEVC support
Francois Cartegnie
git at videolan.org
Fri Nov 24 13:12:04 CET 2017
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon Oct 2 17:20:39 2017 +0200| [150c37c240c0a3f6c4d110d1ac51127a27beab58] | committer: Thomas Guillem
codec: videotoolbox: add HEVC support
Fixes #18855
Signed-off-by: Thomas Guillem <thomas at gllm.fr>
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=150c37c240c0a3f6c4d110d1ac51127a27beab58
---
modules/codec/hxxx_helper.c | 11 ++-
modules/codec/videotoolbox.m | 221 +++++++++++++++++++++++++++++++++++++++++--
2 files changed, 224 insertions(+), 8 deletions(-)
diff --git a/modules/codec/hxxx_helper.c b/modules/codec/hxxx_helper.c
index 69a031234a..b5ec806e0d 100644
--- a/modules/codec/hxxx_helper.c
+++ b/modules/codec/hxxx_helper.c
@@ -878,8 +878,15 @@ hxxx_helper_get_colorimetry(const struct hxxx_helper *hh,
== true ? VLC_SUCCESS : VLC_EGENERIC;
}
case VLC_CODEC_HEVC:
- /* FIXME */
- return VLC_EGENERIC;
+ {
+ const struct hxxx_helper_nal *hsps = &hh->hevc.sps_list[hh->hevc.i_current_sps];
+ if (hsps == NULL)
+ return VLC_EGENERIC;
+
+ return hevc_get_colorimetry(hsps->hevc_sps, p_primaries, p_transfer,
+ p_colorspace, p_full_range)
+ == true ? VLC_SUCCESS : VLC_EGENERIC;
+ }
default:
vlc_assert_unreachable();
}
diff --git a/modules/codec/videotoolbox.m b/modules/codec/videotoolbox.m
index b1981a57cb..2f2082dcbb 100644
--- a/modules/codec/videotoolbox.m
+++ b/modules/codec/videotoolbox.m
@@ -179,6 +179,7 @@ struct decoder_sys_t
int i_forced_cvpx_format;
h264_poc_context_t h264_pocctx;
+ hevc_poc_ctx_t hevc_pocctx;
date_t pts;
};
@@ -512,6 +513,192 @@ static bool VideoToolboxNeedsToRestartH264(decoder_t *p_dec,
return b_ret;
}
+static bool InitHEVC(decoder_t *p_dec)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ hevc_poc_cxt_init(&p_sys->hevc_pocctx);
+ hxxx_helper_init(&p_sys->hh, VLC_OBJECT(p_dec),
+ p_dec->fmt_in.i_codec, true);
+ return hxxx_helper_set_extra(&p_sys->hh, p_dec->fmt_in.p_extra,
+ p_dec->fmt_in.i_extra) == VLC_SUCCESS;
+}
+
+#define CleanHEVC CleanH264
+
+static void GetxPSHEVC(uint8_t i_id, void *priv,
+ hevc_picture_parameter_set_t **pp_pps,
+ hevc_sequence_parameter_set_t **pp_sps,
+ hevc_video_parameter_set_t **pp_vps)
+{
+ decoder_sys_t *p_sys = priv;
+
+ *pp_pps = p_sys->hh.hevc.pps_list[i_id].hevc_pps;
+ if (*pp_pps == NULL)
+ {
+ *pp_vps = NULL;
+ *pp_sps = NULL;
+ }
+ else
+ {
+ uint8_t i_sps_id = hevc_get_pps_sps_id(*pp_pps);
+ *pp_sps = p_sys->hh.hevc.sps_list[i_sps_id].hevc_sps;
+ if (*pp_sps == NULL)
+ *pp_vps = NULL;
+ else
+ {
+ uint8_t i_vps_id = hevc_get_sps_vps_id(*pp_sps);
+ *pp_vps = p_sys->hh.hevc.vps_list[i_vps_id].hevc_vps;
+ }
+ }
+}
+
+struct hevc_sei_callback_s
+{
+ hevc_sei_pic_timing_t *p_timing;
+ const hevc_sequence_parameter_set_t *p_sps;
+};
+
+static bool ParseHEVCSEI(const hxxx_sei_data_t *p_sei_data, void *priv)
+{
+ if(p_sei_data->i_type == HXXX_SEI_PIC_TIMING)
+ {
+ struct hevc_sei_callback_s *s = priv;
+ if(likely(s->p_sps))
+ s->p_timing = hevc_decode_sei_pic_timing(p_sei_data->p_bs, s->p_sps);
+ return false;
+ }
+ return true;
+}
+
+static bool FillReorderInfoHEVC(decoder_t *p_dec, const block_t *p_block,
+ frame_info_t *p_info)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ hxxx_iterator_ctx_t itctx;
+ hxxx_iterator_init(&itctx, p_block->p_buffer, p_block->i_buffer,
+ p_sys->hh.i_nal_length_size);
+
+ const uint8_t *p_nal; size_t i_nal;
+ const uint8_t *p_sei_nal = NULL; size_t i_sei_nal = 0;
+ while(hxxx_iterate_next(&itctx, &p_nal, &i_nal))
+ {
+ if(i_nal < 2 || hevc_getNALLayer(p_nal) > 0)
+ continue;
+
+ const enum hevc_nal_unit_type_e i_nal_type = hevc_getNALType(p_nal);
+ if (i_nal_type <= HEVC_NAL_IRAP_VCL23)
+ {
+ hevc_slice_segment_header_t *p_sli =
+ hevc_decode_slice_header(p_nal, i_nal, true, GetxPSHEVC, p_sys);
+
+ hevc_sequence_parameter_set_t *p_sps;
+ hevc_picture_parameter_set_t *p_pps;
+ hevc_video_parameter_set_t *p_vps;
+ GetxPSHEVC(hevc_get_slice_pps_id(p_sli), p_sys, &p_pps, &p_sps, &p_vps);
+ if(p_sps)
+ {
+ struct hevc_sei_callback_s sei;
+ sei.p_sps = p_sps;
+ sei.p_timing = NULL;
+
+ if(!p_sys->b_invalid_pic_reorder_max && p_vps)
+ {
+ vlc_mutex_lock(&p_sys->lock);
+ p_sys->i_pic_reorder_max = hevc_get_max_num_reorder(p_vps);
+ vlc_mutex_unlock(&p_sys->lock);
+ }
+
+ const int POC = hevc_compute_picture_order_count(p_sps, p_sli,
+ &p_sys->hevc_pocctx);
+
+ if(p_sei_nal)
+ HxxxParseSEI(p_sei_nal, i_sei_nal, 1, ParseHEVCSEI, &sei);
+
+ p_info->i_poc = POC;
+ p_info->i_foc = POC; /* clearly looks wrong :/ */
+ p_info->i_num_ts = hevc_get_num_clock_ts(p_sps, sei.p_timing);
+ p_info->b_flush = (POC == 0);
+ p_info->b_field = (p_info->i_num_ts == 1);
+ p_info->b_progressive = hevc_frame_is_progressive(p_sps, sei.p_timing);
+
+ /* Set frame rate for timings in case of missing rate */
+ if( (!p_dec->fmt_in.video.i_frame_rate_base ||
+ !p_dec->fmt_in.video.i_frame_rate) )
+ {
+ unsigned num, den;
+ if(hevc_get_frame_rate(p_sps, p_vps, &num, &den))
+ date_Change(&p_sys->pts, num, den);
+ }
+
+ if(sei.p_timing)
+ hevc_release_sei_pic_timing(sei.p_timing);
+ }
+
+ return true; /* No need to parse further NAL */
+ }
+ else if(i_nal_type == HEVC_NAL_PREF_SEI)
+ {
+ p_sei_nal = p_nal;
+ i_sei_nal = i_nal;
+ }
+ }
+
+ return false;
+}
+
+static bool VideoToolboxNeedsToRestartHEVC(decoder_t *p_dec,
+ VTDecompressionSessionRef session)
+{
+ VLC_UNUSED(p_dec);
+ VLC_UNUSED(session);
+ return false;
+}
+
+static CFMutableDictionaryRef GetDecoderExtradataHEVC(decoder_t *p_dec)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+
+ CFMutableDictionaryRef extradata = nil;
+ if(p_dec->fmt_in.i_extra) /* copy DecoderConfiguration */
+ {
+ extradata = ExtradataInfoCreate(CFSTR("hvcC"),
+ p_dec->fmt_in.p_extra,
+ p_dec->fmt_in.i_extra);
+ }
+ else if (p_sys->hh.hevc.i_pps_count &&
+ p_sys->hh.hevc.i_sps_count &&
+ p_sys->hh.hevc.i_vps_count)
+ {
+ /* build DecoderConfiguration from gathered */
+ block_t *p_hvcC = hevc_helper_get_hvcc_config(&p_sys->hh);
+ if (p_hvcC)
+ {
+ extradata = ExtradataInfoCreate(CFSTR("hvcC"),
+ p_hvcC->p_buffer,
+ p_hvcC->i_buffer);
+ block_Release(p_hvcC);
+ }
+ }
+ return extradata;
+}
+
+static bool LateStartHEVC(decoder_t *p_dec)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ return (p_dec->fmt_in.i_extra == 0 &&
+ (!p_sys->hh.hevc.i_pps_count ||
+ !p_sys->hh.hevc.i_sps_count ||
+ !p_sys->hh.hevc.i_vps_count) );
+}
+
+static bool CodecSupportedHEVC(decoder_t *p_dec)
+{
+ return true;
+}
+
+#define ConfigureVoutHEVC ConfigureVoutH264
+#define ProcessBlockHEVC ProcessBlockH264
+
static CFMutableDictionaryRef GetDecoderExtradataMPEG4(decoder_t *p_dec)
{
if (p_dec->fmt_in.i_extra)
@@ -714,6 +901,14 @@ static CMVideoCodecType CodecPrecheck(decoder_t *p_dec)
case VLC_CODEC_H264:
return kCMVideoCodecType_H264;
+ case VLC_CODEC_HEVC:
+ if (!deviceSupportsHEVC())
+ {
+ msg_Warn(p_dec, "device doesn't support HEVC");
+ return -1;
+ }
+ return kCMVideoCodecType_HEVC;
+
case VLC_CODEC_MP4V:
{
if (p_dec->fmt_in.i_original_fourcc == VLC_FOURCC( 'X','V','I','D' )) {
@@ -1093,6 +1288,19 @@ static int OpenDecoder(vlc_object_t *p_this)
p_sys->b_poc_based_reorder = true;
break;
+ case kCMVideoCodecType_HEVC:
+ p_sys->pf_codec_init = InitHEVC;
+ p_sys->pf_codec_clean = CleanHEVC;
+ p_sys->pf_codec_supported = CodecSupportedHEVC;
+ p_sys->pf_late_start = LateStartHEVC;
+ p_sys->pf_process_block = ProcessBlockHEVC;
+ p_sys->pf_need_restart = VideoToolboxNeedsToRestartHEVC;
+ p_sys->pf_configure_vout = ConfigureVoutHEVC;
+ p_sys->pf_get_extradata = GetDecoderExtradataHEVC;
+ p_sys->pf_fill_reorder_info = FillReorderInfoHEVC;
+ p_sys->b_poc_based_reorder = true;
+ break;
+
case kCMVideoCodecType_MPEG4Video:
p_sys->pf_get_extradata = GetDecoderExtradataMPEG4;
break;
@@ -1102,22 +1310,23 @@ static int OpenDecoder(vlc_object_t *p_this)
break;
}
- if(p_sys->pf_codec_init && !p_sys->pf_codec_init(p_dec))
+ if (p_sys->pf_codec_init && !p_sys->pf_codec_init(p_dec))
+ {
+ CloseDecoder(p_this);
+ return VLC_EGENERIC;
+ }
+ if (p_sys->pf_codec_supported && !p_sys->pf_codec_supported(p_dec))
{
CloseDecoder(p_this);
return VLC_EGENERIC;
}
int i_ret = StartVideoToolbox(p_dec);
- if(i_ret == VLC_SUCCESS)
- {
+ if (i_ret == VLC_SUCCESS)
msg_Info(p_dec, "Using Video Toolbox to decode '%4.4s'",
(char *)&p_dec->fmt_in.i_codec);
- }
else
- {
CloseDecoder(p_this);
- }
return i_ret;
}
More information about the vlc-commits
mailing list