/***************************************************************************** * hevc.c: h.265/hevc video packetizer ***************************************************************************** * Copyright (C) 2014 VLC authors and VideoLAN * $Id$ * * Authors: Denis Charmet * * 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include "packetizer_helper.h" #include "startcode_helper.h" #include "hevc_nal.h" #include "hxxx_nal.h" #include "hxxx_sei.h" #include "hxxx_common.h" #include /***************************************************************************** * Module descriptor *****************************************************************************/ static int Open (vlc_object_t *); static void Close(vlc_object_t *); vlc_module_begin () set_category(CAT_SOUT) set_subcategory(SUBCAT_SOUT_PACKETIZER) set_description(N_("HEVC/H.265 video packetizer")) set_capability("packetizer", 50) set_callbacks(Open, Close) vlc_module_end () /**************************************************************************** * Local prototypes ****************************************************************************/ static block_t *PacketizeAnnexB(decoder_t *, block_t **); static block_t *PacketizeHVC1(decoder_t *, block_t **); static void PacketizeFlush( decoder_t * ); static void PacketizeReset(void *p_private, bool b_broken); static block_t *PacketizeParse(void *p_private, bool *pb_ts_used, block_t *); static block_t *ParseNALBlock(decoder_t *, bool *pb_ts_used, block_t *); static int PacketizeValidate(void *p_private, block_t *); static block_t * PacketizeDrain(void *); static bool ParseSEICallback( const hxxx_sei_data_t *, void * ); static block_t *GetCc( decoder_t *, decoder_cc_desc_t * ); struct decoder_sys_t { /* */ packetizer_t packetizer; struct { block_t *p_chain; block_t **pp_chain_last; } frame, pre, post; uint8_t i_nal_length_size; struct { block_t *p_nal; void *p_decoded; } rg_vps[HEVC_VPS_ID_MAX + 1], rg_sps[HEVC_SPS_ID_MAX + 1], rg_pps[HEVC_PPS_ID_MAX + 1]; const hevc_video_parameter_set_t *p_active_vps; const hevc_sequence_parameter_set_t *p_active_sps; const hevc_picture_parameter_set_t *p_active_pps; hevc_sei_pic_timing_t *p_timing; bool b_init_sequence_complete; date_t dts; vlc_tick_t pts; bool b_need_ts; /* */ cc_storage_t *p_ccs; }; #define BLOCK_FLAG_DROP (1 << BLOCK_FLAG_PRIVATE_SHIFT) static const uint8_t p_hevc_startcode[3] = {0x00, 0x00, 0x01}; /**************************************************************************** * Helpers ****************************************************************************/ static inline void InitQueue( block_t **pp_head, block_t ***ppp_tail ) { *pp_head = NULL; *ppp_tail = pp_head; } #define INITQ(name) InitQueue(&p_sys->name.p_chain, &p_sys->name.pp_chain_last) static block_t * OutputQueues(decoder_sys_t *p_sys, bool b_valid) { block_t *p_output = NULL; block_t **pp_output_last = &p_output; uint32_t i_flags = 0; /* Because block_ChainGather does not merge flags or times */ if(p_sys->pre.p_chain) { i_flags |= p_sys->pre.p_chain->i_flags; block_ChainLastAppend(&pp_output_last, p_sys->pre.p_chain); INITQ(pre); } if(p_sys->frame.p_chain) { i_flags |= p_sys->frame.p_chain->i_flags; block_ChainLastAppend(&pp_output_last, p_sys->frame.p_chain); p_output->i_dts = date_Get(&p_sys->dts); p_output->i_pts = p_sys->pts; INITQ(frame); } if(p_sys->post.p_chain) { i_flags |= p_sys->post.p_chain->i_flags; block_ChainLastAppend(&pp_output_last, p_sys->post.p_chain); INITQ(post); } if(p_output) { p_output->i_flags |= i_flags; if(!b_valid) p_output->i_flags |= BLOCK_FLAG_DROP; } return p_output; } /***************************************************************************** * Open *****************************************************************************/ static int Open(vlc_object_t *p_this) { decoder_t *p_dec = (decoder_t*)p_this; decoder_sys_t *p_sys; if (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC) return VLC_EGENERIC; p_dec->p_sys = p_sys = calloc(1, sizeof(decoder_sys_t)); if (!p_dec->p_sys) return VLC_ENOMEM; p_sys->p_ccs = cc_storage_new(); if(unlikely(!p_sys->p_ccs)) { free(p_dec->p_sys); return VLC_ENOMEM; } INITQ(pre); INITQ(frame); INITQ(post); packetizer_Init(&p_dec->p_sys->packetizer, p_hevc_startcode, sizeof(p_hevc_startcode), startcode_FindAnnexB, p_hevc_startcode, 1, 5, PacketizeReset, PacketizeParse, PacketizeValidate, PacketizeDrain, p_dec); /* Copy properties */ es_format_Copy(&p_dec->fmt_out, &p_dec->fmt_in); p_dec->fmt_out.b_packetized = true; /* Init timings */ if( p_dec->fmt_in.video.i_frame_rate_base && p_dec->fmt_in.video.i_frame_rate && p_dec->fmt_in.video.i_frame_rate <= UINT_MAX / 2 ) date_Init( &p_sys->dts, p_dec->fmt_in.video.i_frame_rate * 2, p_dec->fmt_in.video.i_frame_rate_base ); else date_Init( &p_sys->dts, 2 * 30000, 1001 ); date_Set( &p_sys->dts, VLC_TICK_INVALID ); p_sys->pts = VLC_TICK_INVALID; p_sys->b_need_ts = true; /* Set callbacks */ const uint8_t *p_extra = p_dec->fmt_in.p_extra; const size_t i_extra = p_dec->fmt_in.i_extra; /* Check if we have hvcC as extradata */ if(hevc_ishvcC(p_extra, i_extra)) { p_dec->pf_packetize = PacketizeHVC1; /* Clear hvcC/HVC1 extra, to be replaced with AnnexB */ free(p_dec->fmt_out.p_extra); p_dec->fmt_out.i_extra = 0; size_t i_new_extra = 0; p_dec->fmt_out.p_extra = hevc_hvcC_to_AnnexB_NAL(p_extra, i_extra, &i_new_extra, &p_sys->i_nal_length_size); if(p_dec->fmt_out.p_extra) p_dec->fmt_out.i_extra = i_new_extra; } else { p_dec->pf_packetize = PacketizeAnnexB; } p_dec->pf_flush = PacketizeFlush; p_dec->pf_get_cc = GetCc; if(p_dec->fmt_out.i_extra) { /* Feed with AnnexB VPS/SPS/PPS/SEI extradata */ packetizer_Header(&p_sys->packetizer, p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra); } return VLC_SUCCESS; } /***************************************************************************** * Close *****************************************************************************/ static void Close(vlc_object_t *p_this) { decoder_t *p_dec = (decoder_t*)p_this; decoder_sys_t *p_sys = p_dec->p_sys; packetizer_Clean(&p_sys->packetizer); block_ChainRelease(p_sys->frame.p_chain); block_ChainRelease(p_sys->pre.p_chain); block_ChainRelease(p_sys->post.p_chain); for(unsigned i=0;i<=HEVC_PPS_ID_MAX; i++) { if(p_sys->rg_pps[i].p_decoded) hevc_rbsp_release_pps(p_sys->rg_pps[i].p_decoded); if(p_sys->rg_pps[i].p_nal) block_Release(p_sys->rg_pps[i].p_nal); } for(unsigned i=0;i<=HEVC_SPS_ID_MAX; i++) { if(p_sys->rg_sps[i].p_decoded) hevc_rbsp_release_sps(p_sys->rg_sps[i].p_decoded); if(p_sys->rg_sps[i].p_nal) block_Release(p_sys->rg_sps[i].p_nal); } for(unsigned i=0;i<=HEVC_VPS_ID_MAX; i++) { if(p_sys->rg_vps[i].p_decoded) hevc_rbsp_release_vps(p_sys->rg_vps[i].p_decoded); if(p_sys->rg_vps[i].p_nal) block_Release(p_sys->rg_vps[i].p_nal); } hevc_release_sei_pic_timing( p_sys->p_timing ); cc_storage_delete( p_sys->p_ccs ); free(p_sys); } /**************************************************************************** * Packetize ****************************************************************************/ static block_t *PacketizeHVC1(decoder_t *p_dec, block_t **pp_block) { decoder_sys_t *p_sys = p_dec->p_sys; return PacketizeXXC1( p_dec, p_sys->i_nal_length_size, pp_block, ParseNALBlock ); } static block_t *PacketizeAnnexB(decoder_t *p_dec, block_t **pp_block) { decoder_sys_t *p_sys = p_dec->p_sys; return packetizer_Packetize(&p_sys->packetizer, pp_block); } static void PacketizeFlush( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; packetizer_Flush( &p_sys->packetizer ); } /***************************************************************************** * GetCc: *****************************************************************************/ static block_t *GetCc( decoder_t *p_dec, decoder_cc_desc_t *p_desc ) { return cc_storage_get_current( p_dec->p_sys->p_ccs, p_desc ); } /**************************************************************************** * Packetizer Helpers ****************************************************************************/ static void PacketizeReset(void *p_private, bool b_broken) { VLC_UNUSED(b_broken); decoder_t *p_dec = p_private; decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_out = OutputQueues(p_sys, false); if(p_out) block_ChainRelease(p_out); p_sys->b_init_sequence_complete = false; p_sys->b_need_ts = true; date_Set(&p_sys->dts, VLC_TICK_INVALID); } static bool InsertXPS(decoder_t *p_dec, uint8_t i_nal_type, uint8_t i_id, const block_t *p_nalb) { decoder_sys_t *p_sys = p_dec->p_sys; void **pp_decoded; void **pp_active; block_t **pp_nal; switch(i_nal_type) { case HEVC_NAL_VPS: if(i_id > HEVC_VPS_ID_MAX) return false; pp_decoded = &p_sys->rg_vps[i_id].p_decoded; pp_nal = &p_sys->rg_vps[i_id].p_nal; pp_active = (void**)&p_sys->p_active_vps; break; case HEVC_NAL_SPS: if(i_id > HEVC_SPS_ID_MAX) return false; pp_decoded = &p_sys->rg_sps[i_id].p_decoded; pp_nal = &p_sys->rg_sps[i_id].p_nal; pp_active = (void**)&p_sys->p_active_sps; break; case HEVC_NAL_PPS: if(i_id > HEVC_PPS_ID_MAX) return false; pp_decoded = &p_sys->rg_pps[i_id].p_decoded; pp_nal = &p_sys->rg_pps[i_id].p_nal; pp_active = (void**)&p_sys->p_active_pps; break; default: return false; } /* Check if we really need to re-decode/replace */ if(*pp_nal) { const uint8_t *p_stored = (*pp_nal)->p_buffer; size_t i_stored = (*pp_nal)->i_buffer; hxxx_strip_AnnexB_startcode(&p_stored, &i_stored); const uint8_t *p_new = p_nalb->p_buffer; size_t i_new = p_nalb->i_buffer; hxxx_strip_AnnexB_startcode(&p_new, &i_new); if(i_stored == i_new && !memcmp(p_stored, p_new, i_new)) return true; } /* Free associated decoded version */ if(*pp_decoded) { switch(i_nal_type) { case HEVC_NAL_VPS: hevc_rbsp_release_vps(*pp_decoded); break; case HEVC_NAL_SPS: hevc_rbsp_release_sps(*pp_decoded); break; case HEVC_NAL_PPS: hevc_rbsp_release_pps(*pp_decoded); break; } if(*pp_active == *pp_decoded) *pp_active = NULL; else pp_active = NULL; /* don't change pointer */ *pp_decoded = NULL; } else pp_active = NULL; /* Free raw stored version */ if(*pp_nal) { block_Release(*pp_nal); *pp_nal = NULL; } const uint8_t *p_buffer = p_nalb->p_buffer; size_t i_buffer = p_nalb->i_buffer; if( hxxx_strip_AnnexB_startcode( &p_buffer, &i_buffer ) ) { /* Create decoded entries */ switch(i_nal_type) { case HEVC_NAL_SPS: *pp_decoded = hevc_decode_sps(p_buffer, i_buffer, true); if(!*pp_decoded) { msg_Err(p_dec, "Failed decoding SPS id %d", i_id); return false; } break; case HEVC_NAL_PPS: *pp_decoded = hevc_decode_pps(p_buffer, i_buffer, true); if(!*pp_decoded) { msg_Err(p_dec, "Failed decoding PPS id %d", i_id); return false; } break; case HEVC_NAL_VPS: *pp_decoded = hevc_decode_vps(p_buffer, i_buffer, true); if(!*pp_decoded) { msg_Err(p_dec, "Failed decoding VPS id %d", i_id); return false; } break; } if(*pp_decoded && pp_active) /* restore active by id */ *pp_active = *pp_decoded; *pp_nal = block_Duplicate((block_t *)p_nalb); return true; } return false; } static bool XPSReady(decoder_sys_t *p_sys) { for(unsigned i=0;i<=HEVC_PPS_ID_MAX; i++) { const hevc_picture_parameter_set_t *p_pps = p_sys->rg_pps[i].p_decoded; if (p_pps) { uint8_t id_sps = hevc_get_pps_sps_id(p_pps); const hevc_sequence_parameter_set_t *p_sps = p_sys->rg_sps[id_sps].p_decoded; if(p_sps) { uint8_t id_vps = hevc_get_sps_vps_id(p_sps); if(p_sys->rg_vps[id_vps].p_decoded) return true; } } } return false; } static void AppendAsAnnexB(const block_t *p_block, uint8_t **pp_dst, size_t *pi_dst) { if(SIZE_MAX - p_block->i_buffer < *pi_dst ) return; size_t i_realloc = p_block->i_buffer + *pi_dst; uint8_t *p_realloc = realloc(*pp_dst, i_realloc); if(p_realloc) { memcpy(&p_realloc[*pi_dst], p_block->p_buffer, p_block->i_buffer); *pi_dst = i_realloc; *pp_dst = p_realloc; } } #define APPENDIF(idmax, set, rg, b) \ for(size_t i=0; i<=idmax; i++)\ {\ if(((set != rg[i].p_decoded) == !b) && rg[i].p_nal)\ {\ AppendAsAnnexB(rg[i].p_nal, &p_data, &i_data);\ if(b) break;\ }\ } static void SetsToAnnexB(decoder_sys_t *p_sys, const hevc_picture_parameter_set_t *p_pps, const hevc_sequence_parameter_set_t *p_sps, const hevc_video_parameter_set_t *p_vps, uint8_t **pp_out, int *pi_out) { uint8_t *p_data = NULL; size_t i_data = 0; APPENDIF(HEVC_VPS_ID_MAX, p_vps, p_sys->rg_vps, true); APPENDIF(HEVC_VPS_ID_MAX, p_vps, p_sys->rg_vps, false); APPENDIF(HEVC_SPS_ID_MAX, p_sps, p_sys->rg_sps, true); APPENDIF(HEVC_SPS_ID_MAX, p_sps, p_sys->rg_sps, false); APPENDIF(HEVC_PPS_ID_MAX, p_pps, p_sys->rg_pps, true); APPENDIF(HEVC_PPS_ID_MAX, p_pps, p_sys->rg_pps, false); /* because we copy to i_extra :/ */ if(i_data <= INT_MAX) { *pp_out = p_data; *pi_out = i_data; } else free(p_data); } static void ActivateSets(decoder_t *p_dec, const hevc_picture_parameter_set_t *p_pps, const hevc_sequence_parameter_set_t *p_sps, const hevc_video_parameter_set_t *p_vps) { decoder_sys_t *p_sys = p_dec->p_sys; p_sys->p_active_pps = p_pps; p_sys->p_active_sps = p_sps; p_sys->p_active_vps = p_vps; if(p_sps) { if(!p_dec->fmt_out.video.i_frame_rate || !p_dec->fmt_out.video.i_frame_rate_base) { unsigned num, den; if(hevc_get_frame_rate( p_sps, p_vps, &num, &den )) { p_dec->fmt_out.video.i_frame_rate = num; p_dec->fmt_out.video.i_frame_rate_base = den; if(num <= UINT_MAX / 2 && (p_sys->dts.i_divider_den != den || p_sys->dts.i_divider_num != 2 * num)) { date_Change(&p_sys->dts, 2 * num, den); } } p_dec->fmt_out.video.i_frame_rate = p_sys->dts.i_divider_num >> 1; p_dec->fmt_out.video.i_frame_rate_base = p_sys->dts.i_divider_den; } if(p_dec->fmt_in.video.primaries == COLOR_PRIMARIES_UNDEF) { (void) hevc_get_colorimetry( p_sps, &p_dec->fmt_out.video.primaries, &p_dec->fmt_out.video.transfer, &p_dec->fmt_out.video.space, &p_dec->fmt_out.video.b_color_range_full); } unsigned sizes[4]; if( hevc_get_picture_size( p_sps, &sizes[0], &sizes[1], &sizes[2], &sizes[3] ) ) { p_dec->fmt_out.video.i_width = sizes[0]; p_dec->fmt_out.video.i_height = sizes[1]; if(p_dec->fmt_in.video.i_visible_width == 0) { p_dec->fmt_out.video.i_visible_width = sizes[2]; p_dec->fmt_out.video.i_visible_height = sizes[3]; } } if(p_dec->fmt_in.i_profile == -1) { uint8_t i_profile, i_level; if( hevc_get_sps_profile_tier_level( p_sps, &i_profile, &i_level ) ) { p_dec->fmt_out.i_profile = i_profile; p_dec->fmt_out.i_level = i_level; } } if(p_dec->fmt_out.i_extra == 0 && p_vps && p_pps) SetsToAnnexB(p_sys, p_pps, p_sps, p_vps, (uint8_t **)&p_dec->fmt_out.p_extra, &p_dec->fmt_out.i_extra); } } static void GetXPSSet(uint8_t i_pps_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_sps = NULL; *pp_vps = NULL; if((*pp_pps = p_sys->rg_pps[i_pps_id].p_decoded)) if((*pp_sps = p_sys->rg_sps[hevc_get_pps_sps_id(*pp_pps)].p_decoded)) *pp_vps = p_sys->rg_vps[hevc_get_sps_vps_id(*pp_sps)].p_decoded; } static void ParseStoredSEI( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; for( block_t *p_nal = p_sys->pre.p_chain; p_nal; p_nal = p_nal->p_next ) { if( p_nal->i_buffer < 5 ) continue; if( hevc_getNALType(&p_nal->p_buffer[4]) == HEVC_NAL_PREF_SEI ) { HxxxParse_AnnexB_SEI( p_nal->p_buffer, p_nal->i_buffer, 2 /* nal header */, ParseSEICallback, p_dec ); } } } static block_t *ParseVCL(decoder_t *p_dec, uint8_t i_nal_type, block_t *p_frag) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_outputchain = NULL; const uint8_t *p_buffer = p_frag->p_buffer; size_t i_buffer = p_frag->i_buffer; if(unlikely(!hxxx_strip_AnnexB_startcode(&p_buffer, &i_buffer) || i_buffer < 3)) { block_ChainLastAppend(&p_sys->frame.pp_chain_last, p_frag); /* might be corrupted */ return NULL; } const uint8_t i_layer = hevc_getNALLayer( p_buffer ); bool b_first_slice_in_pic = p_buffer[2] & 0x80; if (b_first_slice_in_pic) { if(p_sys->frame.p_chain) { /* Starting new frame: return previous frame data for output */ p_outputchain = OutputQueues(p_sys, p_sys->b_init_sequence_complete); } hevc_slice_segment_header_t *p_sli = hevc_decode_slice_header(p_buffer, i_buffer, true, GetXPSSet, p_sys); if(p_sli && i_layer == 0) { hevc_sequence_parameter_set_t *p_sps; hevc_picture_parameter_set_t *p_pps; hevc_video_parameter_set_t *p_vps; GetXPSSet(hevc_get_slice_pps_id(p_sli), p_sys, &p_pps, &p_sps, &p_vps); ActivateSets(p_dec, p_pps, p_sps, p_vps); } ParseStoredSEI( p_dec ); switch(i_nal_type) { case HEVC_NAL_BLA_W_LP: case HEVC_NAL_BLA_W_RADL: case HEVC_NAL_BLA_N_LP: case HEVC_NAL_IDR_W_RADL: case HEVC_NAL_IDR_N_LP: case HEVC_NAL_CRA: p_frag->i_flags |= BLOCK_FLAG_TYPE_I; break; default: { if(p_sli) { enum hevc_slice_type_e type; if(hevc_get_slice_type( p_sli, &type )) { switch(type) { case HEVC_SLICE_TYPE_B: p_frag->i_flags |= BLOCK_FLAG_TYPE_B; break; case HEVC_SLICE_TYPE_P: p_frag->i_flags |= BLOCK_FLAG_TYPE_P; break; case HEVC_SLICE_TYPE_I: p_frag->i_flags |= BLOCK_FLAG_TYPE_I; break; } } } else p_frag->i_flags |= BLOCK_FLAG_TYPE_B; } break; } if(p_sli) hevc_rbsp_release_slice_header(p_sli); } if(!p_sys->b_init_sequence_complete && i_layer == 0 && (p_frag->i_flags & BLOCK_FLAG_TYPE_I) && XPSReady(p_sys)) { p_sys->b_init_sequence_complete = true; } if( !p_sys->b_init_sequence_complete ) cc_storage_reset( p_sys->p_ccs ); block_ChainLastAppend(&p_sys->frame.pp_chain_last, p_frag); return p_outputchain; } static block_t * ParseAUHead(decoder_t *p_dec, uint8_t i_nal_type, block_t *p_nalb) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_ret = NULL; if(p_sys->post.p_chain || p_sys->frame.p_chain) p_ret = OutputQueues(p_sys, p_sys->b_init_sequence_complete); switch(i_nal_type) { case HEVC_NAL_AUD: if(!p_ret && p_sys->pre.p_chain) p_ret = OutputQueues(p_sys, p_sys->b_init_sequence_complete); break; case HEVC_NAL_VPS: case HEVC_NAL_SPS: case HEVC_NAL_PPS: { uint8_t i_id; const uint8_t *p_xps = p_nalb->p_buffer; size_t i_xps = p_nalb->i_buffer; if(hxxx_strip_AnnexB_startcode(&p_xps, &i_xps) && hevc_get_xps_id(p_xps, i_xps, &i_id)) InsertXPS(p_dec, i_nal_type, i_id, p_nalb); break; } case HEVC_NAL_PREF_SEI: /* stored an parsed later when we get sps & frame */ default: break; } block_ChainLastAppend(&p_sys->pre.pp_chain_last, p_nalb); return p_ret; } static block_t * ParseAUTail(decoder_t *p_dec, uint8_t i_nal_type, block_t *p_nalb) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_ret = NULL; block_ChainLastAppend(&p_sys->post.pp_chain_last, p_nalb); switch(i_nal_type) { case HEVC_NAL_EOS: case HEVC_NAL_EOB: p_ret = OutputQueues(p_sys, p_sys->b_init_sequence_complete); if( p_ret ) p_ret->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE; break; case HEVC_NAL_SUFF_SEI: HxxxParse_AnnexB_SEI( p_nalb->p_buffer, p_nalb->i_buffer, 2 /* nal header */, ParseSEICallback, p_dec ); break; } if(!p_ret && p_sys->frame.p_chain == NULL) p_ret = OutputQueues(p_sys, false); return p_ret; } static block_t * ParseNonVCL(decoder_t *p_dec, uint8_t i_nal_type, block_t *p_nalb) { block_t *p_ret = NULL; if ( (i_nal_type >= HEVC_NAL_VPS && i_nal_type <= HEVC_NAL_AUD) || i_nal_type == HEVC_NAL_PREF_SEI || (i_nal_type >= HEVC_NAL_RSV_NVCL41 && i_nal_type <= HEVC_NAL_RSV_NVCL44) || (i_nal_type >= HEVC_NAL_UNSPEC48 && i_nal_type <= HEVC_NAL_UNSPEC55) ) { p_ret = ParseAUHead(p_dec, i_nal_type, p_nalb); } else { p_ret = ParseAUTail(p_dec, i_nal_type, p_nalb); } return p_ret; } static block_t *GatherAndValidateChain(block_t *p_outputchain) { block_t *p_output = NULL; if(p_outputchain) { if(p_outputchain->i_flags & BLOCK_FLAG_DROP) p_output = p_outputchain; /* Avoid useless gather */ else p_output = block_ChainGather(p_outputchain); } if(p_output && (p_output->i_flags & BLOCK_FLAG_DROP)) { block_ChainRelease(p_output); /* Chain! see above */ p_output = NULL; } return p_output; } static void SetOutputBlockProperties(decoder_t *p_dec, block_t *p_output) { decoder_sys_t *p_sys = p_dec->p_sys; /* Set frame duration */ if(p_sys->p_active_sps) { uint8_t i_num_clock_ts = hevc_get_num_clock_ts(p_sys->p_active_sps, p_sys->p_timing); const vlc_tick_t i_start = date_Get(&p_sys->dts); if( i_start != VLC_TICK_INVALID ) { date_Increment(&p_sys->dts, i_num_clock_ts); p_output->i_length = date_Get(&p_sys->dts) - i_start; } p_sys->pts = VLC_TICK_INVALID; } hevc_release_sei_pic_timing(p_sys->p_timing); p_sys->p_timing = NULL; } /***************************************************************************** * ParseNALBlock: parses annexB type NALs * All p_frag blocks are required to start with 0 0 0 1 4-byte startcode *****************************************************************************/ static block_t *ParseNALBlock(decoder_t *p_dec, bool *pb_ts_used, block_t *p_frag) { decoder_sys_t *p_sys = p_dec->p_sys; *pb_ts_used = false; if(p_sys->b_need_ts) { if(p_frag->i_dts > VLC_TICK_INVALID) date_Set(&p_sys->dts, p_frag->i_dts); p_sys->pts = p_frag->i_pts; if(date_Get( &p_sys->dts ) != VLC_TICK_INVALID) p_sys->b_need_ts = false; *pb_ts_used = true; } if(unlikely(p_frag->i_buffer < 5)) { msg_Warn(p_dec,"NAL too small"); block_Release(p_frag); return NULL; } if(p_frag->p_buffer[4] & 0x80) { msg_Warn(p_dec,"Forbidden zero bit not null, corrupted NAL"); block_Release(p_frag); return GatherAndValidateChain(OutputQueues(p_sys, false)); /* will drop */ } /* Get NALU type */ const vlc_tick_t dts = p_frag->i_dts, pts = p_frag->i_pts; block_t * p_output = NULL; uint8_t i_nal_type = hevc_getNALType(&p_frag->p_buffer[4]); if (i_nal_type < HEVC_NAL_VPS) { /* NAL is a VCL NAL */ p_output = ParseVCL(p_dec, i_nal_type, p_frag); if (p_output && (p_output->i_flags & BLOCK_FLAG_DROP)) msg_Info(p_dec, "Waiting for VPS/SPS/PPS"); } else { p_output = ParseNonVCL(p_dec, i_nal_type, p_frag); } p_output = GatherAndValidateChain(p_output); if(p_output) { SetOutputBlockProperties( p_dec, p_output ); if (dts > VLC_TICK_INVALID) date_Set(&p_sys->dts, dts); p_sys->pts = pts; *pb_ts_used = true; } return p_output; } static block_t *PacketizeParse(void *p_private, bool *pb_ts_used, block_t *p_block) { decoder_t *p_dec = p_private; decoder_sys_t *p_sys = p_dec->p_sys; /* Remove trailing 0 bytes */ while (p_block->i_buffer > 5 && p_block->p_buffer[p_block->i_buffer-1] == 0x00 ) p_block->i_buffer--; p_block = ParseNALBlock( p_dec, pb_ts_used, p_block ); if( p_block ) cc_storage_commit( p_sys->p_ccs, p_block ); return p_block; } static int PacketizeValidate( void *p_private, block_t *p_au ) { VLC_UNUSED(p_private); VLC_UNUSED(p_au); return VLC_SUCCESS; } static block_t * PacketizeDrain(void *p_private) { decoder_t *p_dec = p_private; decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_out = NULL; if( p_sys->frame.p_chain && p_sys->b_init_sequence_complete ) { p_out = OutputQueues(p_sys, true); if( p_out ) { p_out = GatherAndValidateChain(p_out); if( p_out ) SetOutputBlockProperties( p_dec, p_out ); } } return p_out; } static bool ParseSEICallback( const hxxx_sei_data_t *p_sei_data, void *cbdata ) { decoder_t *p_dec = (decoder_t *) cbdata; decoder_sys_t *p_sys = p_dec->p_sys; switch( p_sei_data->i_type ) { case HXXX_SEI_PIC_TIMING: { if( p_sys->p_active_sps ) { hevc_release_sei_pic_timing( p_sys->p_timing ); p_sys->p_timing = hevc_decode_sei_pic_timing( p_sei_data->p_bs, p_sys->p_active_sps ); } } break; case HXXX_SEI_USER_DATA_REGISTERED_ITU_T_T35: { if( p_sei_data->itu_t35.type == HXXX_ITU_T35_TYPE_CC ) { cc_storage_append( p_sys->p_ccs, true, p_sei_data->itu_t35.u.cc.p_data, p_sei_data->itu_t35.u.cc.i_data ); } } break; case HXXX_SEI_FRAME_PACKING_ARRANGEMENT: { if( p_dec->fmt_in.video.multiview_mode == MULTIVIEW_2D ) { video_multiview_mode_t mode; switch( p_sei_data->frame_packing.type ) { case FRAME_PACKING_INTERLEAVED_CHECKERBOARD: mode = MULTIVIEW_STEREO_CHECKERBOARD; break; case FRAME_PACKING_INTERLEAVED_COLUMN: mode = MULTIVIEW_STEREO_COL; break; case FRAME_PACKING_INTERLEAVED_ROW: mode = MULTIVIEW_STEREO_ROW; break; case FRAME_PACKING_SIDE_BY_SIDE: mode = MULTIVIEW_STEREO_SBS; break; case FRAME_PACKING_TOP_BOTTOM: mode = MULTIVIEW_STEREO_TB; break; case FRAME_PACKING_TEMPORAL: mode = MULTIVIEW_STEREO_FRAME; break; case FRAME_PACKING_TILED: default: mode = MULTIVIEW_2D; break; } p_dec->fmt_out.video.multiview_mode = mode; } } break; case HXXX_SEI_MASTERING_DISPLAY_COLOUR_VOLUME: { video_format_t *p_fmt = &p_dec->fmt_out.video; for (size_t i=0; icolour_volume.primaries); ++i) p_fmt->mastering.primaries[i] = p_sei_data->colour_volume.primaries[i]; for (size_t i=0; icolour_volume.white_point); ++i) p_fmt->mastering.white_point[i] = p_sei_data->colour_volume.white_point[i]; p_fmt->mastering.max_luminance = p_sei_data->colour_volume.max_luminance; p_fmt->mastering.min_luminance = p_sei_data->colour_volume.min_luminance; } break; case HXXX_SEI_CONTENT_LIGHT_LEVEL: { video_format_t *p_fmt = &p_dec->fmt_out.video; p_fmt->lighting.MaxCLL = p_sei_data->content_light_lvl.MaxCLL; p_fmt->lighting.MaxFALL = p_sei_data->content_light_lvl.MaxFALL; } break; } return true; }