From: Jaroslav Kysela Date: Fri, 18 Sep 2015 14:11:38 +0000 (+0200) Subject: muxers: mkv/libav add HEVC support X-Git-Tag: v4.2.1~2096 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b8219cffc95eaa8eb8344bb3e78a9ef5bcfe2217;p=thirdparty%2Ftvheadend.git muxers: mkv/libav add HEVC support --- diff --git a/Makefile b/Makefile index 3cb1a9495..efe052977 100644 --- a/Makefile +++ b/Makefile @@ -218,6 +218,7 @@ SRCS-2 += \ src/parsers/parsers.c \ src/parsers/bitstream.c \ src/parsers/parser_h264.c \ + src/parsers/parser_hevc.c \ src/parsers/parser_latm.c \ src/parsers/parser_avc.c \ src/parsers/parser_teletext.c \ diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index 7f4cd63d8..4091e0525 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -34,6 +34,7 @@ typedef struct lav_muxer { muxer_t; AVFormatContext *lm_oc; AVBitStreamFilterContext *lm_h264_filter; + AVBitStreamFilterContext *lm_hevc_filter; int lm_fd; int lm_init; } lav_muxer_t; @@ -273,8 +274,10 @@ lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) av_dict_set(&oc->metadata, "service_name", name, 0); av_dict_set(&oc->metadata, "service_provider", app, 0); - if(lm->m_config.m_type == MC_MPEGTS) + if(lm->m_config.m_type == MC_MPEGTS) { lm->lm_h264_filter = av_bitstream_filter_init("h264_mp4toannexb"); + lm->lm_hevc_filter = av_bitstream_filter_init("hevc_mp4toannexb"); + } oc->max_delay = 0.7 * AV_TIME_BASE; @@ -419,10 +422,12 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) tofree = NULL; av_init_packet(&packet); - if(lm->lm_h264_filter && st->codec->codec_id == AV_CODEC_ID_H264) { + if((lm->lm_h264_filter && st->codec->codec_id == AV_CODEC_ID_H264) || + (lm->lm_hevc_filter && st->codec->codec_id == AV_CODEC_ID_HEVC)) { pkt = avc_convert_pkt(opkt = pkt); pkt_ref_dec(opkt); - if(av_bitstream_filter_filter(lm->lm_h264_filter, + if(av_bitstream_filter_filter(st->codec->codec_id == AV_CODEC_ID_H264 ? + lm->lm_h264_filter : lm->lm_hevc_filter, st->codec, NULL, &packet.data, diff --git a/src/muxer/tvh/mkmux.c b/src/muxer/tvh/mkmux.c index 192c95893..3f886cd85 100644 --- a/src/muxer/tvh/mkmux.c +++ b/src/muxer/tvh/mkmux.c @@ -34,6 +34,7 @@ #include "ebml.h" #include "lang_codes.h" #include "parsers/parser_avc.h" +#include "parsers/parser_hevc.h" extern int dvr_iov_max; @@ -49,6 +50,7 @@ TAILQ_HEAD(mk_chapter_queue, mk_chapter); typedef struct mk_track { int index; int avc; + int hevc; int type; int tracknum; int tracktype; @@ -279,6 +281,12 @@ mk_build_tracks(mk_mux_t *mkm, const streaming_start_t *ss) tr->avc = 1; break; + case SCT_HEVC: + tracktype = 1; + codec_id = "V_MPEGH/ISO/HEVC"; + tr->hevc = 1; + break; + case SCT_VP8: tracktype = 1; codec_id = "V_VP8"; @@ -289,10 +297,6 @@ mk_build_tracks(mk_mux_t *mkm, const streaming_start_t *ss) codec_id = "V_VP9"; break; - case SCT_HEVC: - tvherror("mkv", "HEVC (H265) codec is not suppored for Matroska muxer (work in progress)"); - continue; - case SCT_MPEG2AUDIO: tracktype = 2; codec_id = "A_MPEG/L2"; @@ -361,6 +365,9 @@ mk_build_tracks(mk_mux_t *mkm, const streaming_start_t *ss) if (tr->avc) { isom_write_avcc(&hdr, pktbuf_ptr(ssc->ssc_gh), pktbuf_len(ssc->ssc_gh)); + } else if (tr->hevc) { + isom_write_hvcc(&hdr, pktbuf_ptr(ssc->ssc_gh), + pktbuf_len(ssc->ssc_gh)); } else { sbuf_append(&hdr, pktbuf_ptr(ssc->ssc_gh), pktbuf_len(ssc->ssc_gh)); diff --git a/src/parsers/bitstream.c b/src/parsers/bitstream.c index 2ca611d00..9e9fefdb9 100644 --- a/src/parsers/bitstream.c +++ b/src/parsers/bitstream.c @@ -21,29 +21,50 @@ #include "bitstream.h" -void -init_rbits(bitstream_t *bs, const uint8_t *data, int bits) +int +init_rbits(bitstream_t *bs, const uint8_t *data, uint32_t bits) { bs->wdata = NULL; bs->rdata = data; bs->offset = 0; bs->len = bits; + return 0; } -void -init_wbits(bitstream_t *bs, uint8_t *data, int bits) +int +init_wbits(bitstream_t *bs, uint8_t *data, uint32_t bits) { bs->wdata = data; bs->rdata = NULL; bs->offset = 0; bs->len = bits; + return 0; +} + +uint32_t +read_bits(bitstream_t *bs, uint32_t num) +{ + uint32_t r = 0; + + while(num > 0) { + if(bs->offset >= bs->len) + return 0; + + num--; + + if(bs->rdata[bs->offset / 8] & (1 << (7 - (bs->offset & 7)))) + r |= 1 << num; + + bs->offset++; + } + return r; } -unsigned int -read_bits(bitstream_t *bs, int num) +uint64_t +read_bits64(bitstream_t *bs, uint32_t num) { - unsigned int r = 0; + uint64_t r = 0; while(num > 0) { if(bs->offset >= bs->len) @@ -59,10 +80,10 @@ read_bits(bitstream_t *bs, int num) return r; } -unsigned int -show_bits(bitstream_t *bs, int num) +uint32_t +show_bits(bitstream_t *bs, uint32_t num) { - unsigned int r = 0, offset = bs->offset; + uint32_t r = 0, offset = bs->offset; while(num > 0) { if(offset >= bs->len) @@ -78,10 +99,11 @@ show_bits(bitstream_t *bs, int num) return r; } -unsigned int +uint32_t read_golomb_ue(bitstream_t *bs) { - int b, lzb = -1; + uint32_t b; + int lzb = -1; for(b = 0; !b && !bs_eof(bs); lzb++) b = read_bits1(bs); @@ -92,10 +114,10 @@ read_golomb_ue(bitstream_t *bs) } -signed int +int32_t read_golomb_se(bitstream_t *bs) { - int v, pos; + uint32_t v, pos; v = read_golomb_ue(bs); if(v == 0) return 0; @@ -107,7 +129,7 @@ read_golomb_se(bitstream_t *bs) void -put_bits(bitstream_t *bs, int val, int num) +put_bits(bitstream_t *bs, uint32_t val, uint32_t num) { while(num > 0) { if(bs->offset >= bs->len) diff --git a/src/parsers/bitstream.h b/src/parsers/bitstream.h index f4aecf514..4e4d4b960 100644 --- a/src/parsers/bitstream.h +++ b/src/parsers/bitstream.h @@ -22,20 +22,25 @@ typedef struct bitstream { const uint8_t *rdata; uint8_t *wdata; - int offset; - int len; + uint32_t offset; + uint32_t len; } bitstream_t; -static inline void skip_bits(bitstream_t *bs, int num) +static inline void skip_bits(bitstream_t *bs, uint32_t num) { bs->offset += num; } -void init_rbits(bitstream_t *bs, const uint8_t *data, int bits); +static inline void skip_bits1(bitstream_t *bs) + { bs->offset++; } -void init_wbits(bitstream_t *bs, uint8_t *data, int bits); +int init_rbits(bitstream_t *bs, const uint8_t *data, uint32_t bits); -unsigned int read_bits(bitstream_t *gb, int num); +int init_wbits(bitstream_t *bs, uint8_t *data, uint32_t bits); -unsigned int show_bits(bitstream_t *gb, int num); +uint32_t read_bits(bitstream_t *gb, uint32_t num); + +uint64_t read_bits64(bitstream_t *gb, uint32_t num); + +uint32_t show_bits(bitstream_t *gb, uint32_t num); static inline unsigned int read_bits1(bitstream_t *gb) { return read_bits(gb, 1); } @@ -44,12 +49,24 @@ unsigned int read_golomb_ue(bitstream_t *gb); signed int read_golomb_se(bitstream_t *gb); -static inline unsigned int remaining_bits(bitstream_t *gb) +static inline uint32_t remaining_bits(bitstream_t *gb) { return gb->len - gb->offset; } -void put_bits(bitstream_t *bs, int val, int num); +void put_bits(bitstream_t *bs, uint32_t val, uint32_t num); static inline int bs_eof(const bitstream_t *bs) { return bs->offset >= bs->len; } +static inline uint32_t +RB32(const uint8_t *d) +{ + return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; +} + +static inline uint32_t +RB24(const uint8_t *d) +{ + return (d[0] << 16) | (d[1] << 8) | d[2]; +} + #endif /* BITSTREAM_H_ */ diff --git a/src/parsers/parser_avc.c b/src/parsers/parser_avc.c index 87e764113..d23f6d960 100644 --- a/src/parsers/parser_avc.c +++ b/src/parsers/parser_avc.c @@ -20,6 +20,8 @@ */ #include "parser_avc.h" +#include "bitstream.h" + static const uint8_t * avc_find_startcode_internal(const uint8_t *p, const uint8_t *end) { @@ -66,7 +68,7 @@ avc_find_startcode(const uint8_t *p, const uint8_t *end) return out; } -static int avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size) +int avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size) { const uint8_t *p = buf_in; const uint8_t *end = p + size; @@ -91,7 +93,7 @@ static int avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size) return size; } -static int +int avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size) { sbuf_t sb; @@ -105,21 +107,6 @@ avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size) return 0; } - -static inline uint32_t -RB32(const uint8_t *d) -{ - return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; -} - -static inline uint32_t -RB24(const uint8_t *d) -{ - return (d[0] << 16) | (d[1] << 8) | d[2]; -} - -#define FFMIN(a, b) ((a) > (b) ? (b) : (a)) - int isom_write_avcc(sbuf_t *sb, const uint8_t *data, int len) { @@ -143,7 +130,7 @@ isom_write_avcc(sbuf_t *sb, const uint8_t *data, int len) while (end - buf > 4) { unsigned int size; uint8_t nal_type; - size = FFMIN(RB32(buf), end - buf - 4); + size = MIN(RB32(buf), end - buf - 4); buf += 4; nal_type = buf[0] & 0x1f; diff --git a/src/parsers/parser_avc.h b/src/parsers/parser_avc.h index 75d74b671..90618cab2 100644 --- a/src/parsers/parser_avc.h +++ b/src/parsers/parser_avc.h @@ -25,6 +25,10 @@ #include "tvheadend.h" #include "packet.h" +int avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size); + +int avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size); + int isom_write_avcc(sbuf_t *sb, const uint8_t *src, int len); th_pkt_t *avc_convert_pkt(th_pkt_t *src); diff --git a/src/parsers/parser_hevc.c b/src/parsers/parser_hevc.c new file mode 100644 index 000000000..11825ba62 --- /dev/null +++ b/src/parsers/parser_hevc.c @@ -0,0 +1,1171 @@ +/* + * Copyright (c) 2014 Tim Walker + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "parser_hevc.h" +#include "parser_avc.h" +#include "bitstream.h" + +#define NAL_VPS 32 +#define NAL_SPS 33 +#define NAL_PPS 34 +#define NAL_SEI_PREFIX 39 +#define NAL_SEI_SUFFIX 40 + +#define MAX_SPATIAL_SEGMENTATION 4096 // max. value of u(12) field +#define MAX_SUB_LAYERS 7 +#define MAX_VPS_COUNT 16 +#define MAX_SPS_COUNT 32 +#define MAX_PPS_COUNT 256 +#define MAX_SHORT_TERM_RPS_COUNT 64 + +typedef struct HVCCNALUnitArray { + uint8_t NAL_unit_type; + uint16_t numNalus; + uint16_t *nalUnitLength; + uint8_t **nalUnit; +} HVCCNALUnitArray; + +typedef struct HEVCDecoderConfigurationRecord { + uint8_t configurationVersion; + uint8_t general_profile_space; + uint8_t general_tier_flag; + uint8_t general_profile_idc; + uint32_t general_profile_compatibility_flags; + uint64_t general_constraint_indicator_flags; + uint8_t general_level_idc; + uint16_t min_spatial_segmentation_idc; + uint8_t parallelismType; + uint8_t chromaFormat; + uint8_t bitDepthLumaMinus8; + uint8_t bitDepthChromaMinus8; + uint16_t avgFrameRate; + uint8_t constantFrameRate; + uint8_t numTemporalLayers; + uint8_t temporalIdNested; + uint8_t lengthSizeMinusOne; + uint8_t numOfArrays; + HVCCNALUnitArray *array; +} HEVCDecoderConfigurationRecord; + +typedef struct HVCCProfileTierLevel { + uint8_t profile_space; + uint8_t tier_flag; + uint8_t profile_idc; + uint32_t profile_compatibility_flags; + uint64_t constraint_indicator_flags; + uint8_t level_idc; +} HVCCProfileTierLevel; + +static void hvcc_update_ptl(HEVCDecoderConfigurationRecord *hvcc, + HVCCProfileTierLevel *ptl) +{ + /* + * The value of general_profile_space in all the parameter sets must be + * identical. + */ + hvcc->general_profile_space = ptl->profile_space; + + /* + * The level indication general_level_idc must indicate a level of + * capability equal to or greater than the highest level indicated for the + * highest tier in all the parameter sets. + */ + if (hvcc->general_tier_flag < ptl->tier_flag) + hvcc->general_level_idc = ptl->level_idc; + else + hvcc->general_level_idc = MAX(hvcc->general_level_idc, ptl->level_idc); + + /* + * The tier indication general_tier_flag must indicate a tier equal to or + * greater than the highest tier indicated in all the parameter sets. + */ + hvcc->general_tier_flag = MAX(hvcc->general_tier_flag, ptl->tier_flag); + + /* + * The profile indication general_profile_idc must indicate a profile to + * which the stream associated with this configuration record conforms. + * + * If the sequence parameter sets are marked with different profiles, then + * the stream may need examination to determine which profile, if any, the + * entire stream conforms to. If the entire stream is not examined, or the + * examination reveals that there is no profile to which the entire stream + * conforms, then the entire stream must be split into two or more + * sub-streams with separate configuration records in which these rules can + * be met. + * + * Note: set the profile to the highest value for the sake of simplicity. + */ + hvcc->general_profile_idc = MAX(hvcc->general_profile_idc, ptl->profile_idc); + + /* + * Each bit in general_profile_compatibility_flags may only be set if all + * the parameter sets set that bit. + */ + hvcc->general_profile_compatibility_flags &= ptl->profile_compatibility_flags; + + /* + * Each bit in general_constraint_indicator_flags may only be set if all + * the parameter sets set that bit. + */ + hvcc->general_constraint_indicator_flags &= ptl->constraint_indicator_flags; +} + +static void hvcc_parse_ptl(bitstream_t *gb, + HEVCDecoderConfigurationRecord *hvcc, + unsigned int max_sub_layers_minus1) +{ + unsigned int i; + HVCCProfileTierLevel general_ptl; + uint8_t sub_layer_profile_present_flag[MAX_SUB_LAYERS]; + uint8_t sub_layer_level_present_flag[MAX_SUB_LAYERS]; + + general_ptl.profile_space = read_bits(gb, 2); + general_ptl.tier_flag = read_bits1(gb); + general_ptl.profile_idc = read_bits(gb, 5); + general_ptl.profile_compatibility_flags = read_bits(gb, 32); + general_ptl.constraint_indicator_flags = read_bits64(gb, 48); + general_ptl.level_idc = read_bits(gb, 8); + hvcc_update_ptl(hvcc, &general_ptl); + + for (i = 0; i < max_sub_layers_minus1; i++) { + sub_layer_profile_present_flag[i] = read_bits1(gb); + sub_layer_level_present_flag[i] = read_bits1(gb); + } + + if (max_sub_layers_minus1 > 0) + for (i = max_sub_layers_minus1; i < 8; i++) + skip_bits(gb, 2); // reserved_zero_2bits[i] + + for (i = 0; i < max_sub_layers_minus1; i++) { + if (sub_layer_profile_present_flag[i]) { + /* + * sub_layer_profile_space[i] u(2) + * sub_layer_tier_flag[i] u(1) + * sub_layer_profile_idc[i] u(5) + * sub_layer_profile_compatibility_flag[i][0..31] u(32) + * sub_layer_progressive_source_flag[i] u(1) + * sub_layer_interlaced_source_flag[i] u(1) + * sub_layer_non_packed_constraint_flag[i] u(1) + * sub_layer_frame_only_constraint_flag[i] u(1) + * sub_layer_reserved_zero_44bits[i] u(44) + */ + skip_bits(gb, 32); + skip_bits(gb, 32); + skip_bits(gb, 24); + } + + if (sub_layer_level_present_flag[i]) + skip_bits(gb, 8); + } +} + +static void skip_sub_layer_hrd_parameters(bitstream_t *gb, + unsigned int cpb_cnt_minus1, + uint8_t sub_pic_hrd_params_present_flag) +{ + unsigned int i; + + for (i = 0; i <= cpb_cnt_minus1; i++) { + read_golomb_ue(gb); // bit_rate_value_minus1 + read_golomb_ue(gb); // cpb_size_value_minus1 + + if (sub_pic_hrd_params_present_flag) { + read_golomb_ue(gb); // cpb_size_du_value_minus1 + read_golomb_ue(gb); // bit_rate_du_value_minus1 + } + + skip_bits1(gb); // cbr_flag + } +} + +static int skip_hrd_parameters(bitstream_t *gb, uint8_t cprms_present_flag, + unsigned int max_sub_layers_minus1) +{ + unsigned int i; + uint8_t sub_pic_hrd_params_present_flag = 0; + uint8_t nal_hrd_parameters_present_flag = 0; + uint8_t vcl_hrd_parameters_present_flag = 0; + + if (cprms_present_flag) { + nal_hrd_parameters_present_flag = read_bits1(gb); + vcl_hrd_parameters_present_flag = read_bits1(gb); + + if (nal_hrd_parameters_present_flag || + vcl_hrd_parameters_present_flag) { + sub_pic_hrd_params_present_flag = read_bits1(gb); + + if (sub_pic_hrd_params_present_flag) + /* + * tick_divisor_minus2 u(8) + * du_cpb_removal_delay_increment_length_minus1 u(5) + * sub_pic_cpb_params_in_pic_timing_sei_flag u(1) + * dpb_output_delay_du_length_minus1 u(5) + */ + skip_bits(gb, 19); + + /* + * bit_rate_scale u(4) + * cpb_size_scale u(4) + */ + skip_bits(gb, 8); + + if (sub_pic_hrd_params_present_flag) + skip_bits(gb, 4); // cpb_size_du_scale + + /* + * initial_cpb_removal_delay_length_minus1 u(5) + * au_cpb_removal_delay_length_minus1 u(5) + * dpb_output_delay_length_minus1 u(5) + */ + skip_bits(gb, 15); + } + } + + for (i = 0; i <= max_sub_layers_minus1; i++) { + unsigned int cpb_cnt_minus1 = 0; + uint8_t low_delay_hrd_flag = 0; + uint8_t fixed_pic_rate_within_cvs_flag = 0; + uint8_t fixed_pic_rate_general_flag = read_bits1(gb); + + if (!fixed_pic_rate_general_flag) + fixed_pic_rate_within_cvs_flag = read_bits1(gb); + + if (fixed_pic_rate_within_cvs_flag) + read_golomb_ue(gb); // elemental_duration_in_tc_minus1 + else + low_delay_hrd_flag = read_bits1(gb); + + if (!low_delay_hrd_flag) { + cpb_cnt_minus1 = read_golomb_ue(gb); + if (cpb_cnt_minus1 > 31) + return -1; + } + + if (nal_hrd_parameters_present_flag) + skip_sub_layer_hrd_parameters(gb, cpb_cnt_minus1, + sub_pic_hrd_params_present_flag); + + if (vcl_hrd_parameters_present_flag) + skip_sub_layer_hrd_parameters(gb, cpb_cnt_minus1, + sub_pic_hrd_params_present_flag); + } + + return 0; +} + +static void skip_timing_info(bitstream_t *gb) +{ + skip_bits(gb, 32); // num_units_in_tick + skip_bits(gb, 32); // time_scale + + if (read_bits1(gb)) // poc_proportional_to_timing_flag + read_golomb_ue(gb); // num_ticks_poc_diff_one_minus1 +} + +static void hvcc_parse_vui(bitstream_t *gb, + HEVCDecoderConfigurationRecord *hvcc, + unsigned int max_sub_layers_minus1) +{ + unsigned int min_spatial_segmentation_idc; + + if (read_bits1(gb)) // aspect_ratio_info_present_flag + if (read_bits(gb, 8) == 255) // aspect_ratio_idc + skip_bits(gb, 32); // sar_width u(16), sar_height u(16) + + if (read_bits1(gb)) // overscan_info_present_flag + skip_bits1(gb); // overscan_appropriate_flag + + if (read_bits1(gb)) { // video_signal_type_present_flag + skip_bits(gb, 4); // video_format u(3), video_full_range_flag u(1) + + if (read_bits1(gb)) // colour_description_present_flag + /* + * colour_primaries u(8) + * transfer_characteristics u(8) + * matrix_coeffs u(8) + */ + skip_bits(gb, 24); + } + + if (read_bits1(gb)) { // chroma_loc_info_present_flag + read_golomb_ue(gb); // chroma_sample_loc_type_top_field + read_golomb_ue(gb); // chroma_sample_loc_type_bottom_field + } + + /* + * neutral_chroma_indication_flag u(1) + * field_seq_flag u(1) + * frame_field_info_present_flag u(1) + */ + skip_bits(gb, 3); + + if (read_bits1(gb)) { // default_display_window_flag + read_golomb_ue(gb); // def_disp_win_left_offset + read_golomb_ue(gb); // def_disp_win_right_offset + read_golomb_ue(gb); // def_disp_win_top_offset + read_golomb_ue(gb); // def_disp_win_bottom_offset + } + + if (read_bits1(gb)) { // vui_timing_info_present_flag + skip_timing_info(gb); + + if (read_bits1(gb)) // vui_hrd_parameters_present_flag + skip_hrd_parameters(gb, 1, max_sub_layers_minus1); + } + + if (read_bits1(gb)) { // bitstream_restriction_flag + /* + * tiles_fixed_structure_flag u(1) + * motion_vectors_over_pic_boundaries_flag u(1) + * restricted_ref_pic_lists_flag u(1) + */ + skip_bits(gb, 3); + + min_spatial_segmentation_idc = read_golomb_ue(gb); + + /* + * unsigned int(12) min_spatial_segmentation_idc; + * + * The min_spatial_segmentation_idc indication must indicate a level of + * spatial segmentation equal to or less than the lowest level of + * spatial segmentation indicated in all the parameter sets. + */ + hvcc->min_spatial_segmentation_idc = MIN(hvcc->min_spatial_segmentation_idc, + min_spatial_segmentation_idc); + + read_golomb_ue(gb); // max_bytes_per_pic_denom + read_golomb_ue(gb); // max_bits_per_min_cu_denom + read_golomb_ue(gb); // log2_max_mv_length_horizontal + read_golomb_ue(gb); // log2_max_mv_length_vertical + } +} + +static void skip_sub_layer_ordering_info(bitstream_t *gb) +{ + read_golomb_ue(gb); // max_dec_pic_buffering_minus1 + read_golomb_ue(gb); // max_num_reorder_pics + read_golomb_ue(gb); // max_latency_increase_plus1 +} + +static int hvcc_parse_vps(bitstream_t *gb, + HEVCDecoderConfigurationRecord *hvcc) +{ + unsigned int vps_max_sub_layers_minus1; + + /* + * vps_video_parameter_set_id u(4) + * vps_reserved_three_2bits u(2) + * vps_max_layers_minus1 u(6) + */ + skip_bits(gb, 12); + + vps_max_sub_layers_minus1 = read_bits(gb, 3); + + /* + * numTemporalLayers greater than 1 indicates that the stream to which this + * configuration record applies is temporally scalable and the contained + * number of temporal layers (also referred to as temporal sub-layer or + * sub-layer in ISO/IEC 23008-2) is equal to numTemporalLayers. Value 1 + * indicates that the stream is not temporally scalable. Value 0 indicates + * that it is unknown whether the stream is temporally scalable. + */ + hvcc->numTemporalLayers = MAX(hvcc->numTemporalLayers, + vps_max_sub_layers_minus1 + 1); + + /* + * vps_temporal_id_nesting_flag u(1) + * vps_reserved_0xffff_16bits u(16) + */ + skip_bits(gb, 17); + + hvcc_parse_ptl(gb, hvcc, vps_max_sub_layers_minus1); + + /* nothing useful for hvcC past this point */ + return 0; +} + +static void skip_scaling_list_data(bitstream_t *gb) +{ + int i, j, k, num_coeffs; + + for (i = 0; i < 4; i++) + for (j = 0; j < (i == 3 ? 2 : 6); j++) + if (!read_bits1(gb)) // scaling_list_pred_mode_flag[i][j] + read_golomb_ue(gb); // scaling_list_pred_matrix_id_delta[i][j] + else { + num_coeffs = MIN(64, 1 << (4 + (i << 1))); + + if (i > 1) + read_golomb_se(gb); // scaling_list_dc_coef_minus8[i-2][j] + + for (k = 0; k < num_coeffs; k++) + read_golomb_se(gb); // scaling_list_delta_coef + } +} + +static int parse_rps(bitstream_t *gb, unsigned int rps_idx, + unsigned int num_rps, + unsigned int num_delta_pocs[MAX_SHORT_TERM_RPS_COUNT]) +{ + unsigned int i; + + if (rps_idx && read_bits1(gb)) { // inter_ref_pic_set_prediction_flag + /* this should only happen for slice headers, and this isn't one */ + if (rps_idx >= num_rps) + return -1; + + skip_bits1 (gb); // delta_rps_sign + read_golomb_ue(gb); // abs_delta_rps_minus1 + + num_delta_pocs[rps_idx] = 0; + + /* + * From libavcodec/hevc_ps.c: + * + * if (is_slice_header) { + * //foo + * } else + * rps_ridx = &sps->st_rps[rps - sps->st_rps - 1]; + * + * where: + * rps: &sps->st_rps[rps_idx] + * sps->st_rps: &sps->st_rps[0] + * is_slice_header: rps_idx == num_rps + * + * thus: + * if (num_rps != rps_idx) + * rps_ridx = &sps->st_rps[rps_idx - 1]; + * + * NumDeltaPocs[RefRpsIdx]: num_delta_pocs[rps_idx - 1] + */ + for (i = 0; i <= num_delta_pocs[rps_idx - 1]; i++) { + uint8_t use_delta_flag = 0; + uint8_t used_by_curr_pic_flag = read_bits1(gb); + if (!used_by_curr_pic_flag) + use_delta_flag = read_bits1(gb); + + if (used_by_curr_pic_flag || use_delta_flag) + num_delta_pocs[rps_idx]++; + } + } else { + unsigned int num_negative_pics = read_golomb_ue(gb); + unsigned int num_positive_pics = read_golomb_ue(gb); + + if ((num_positive_pics + (uint64_t)num_negative_pics) * 2 > remaining_bits(gb)) + return -1; + + num_delta_pocs[rps_idx] = num_negative_pics + num_positive_pics; + + for (i = 0; i < num_negative_pics; i++) { + read_golomb_ue(gb); // delta_poc_s0_minus1[rps_idx] + skip_bits1 (gb); // used_by_curr_pic_s0_flag[rps_idx] + } + + for (i = 0; i < num_positive_pics; i++) { + read_golomb_ue(gb); // delta_poc_s1_minus1[rps_idx] + skip_bits1 (gb); // used_by_curr_pic_s1_flag[rps_idx] + } + } + + return 0; +} + +static int hvcc_parse_sps(bitstream_t *gb, + HEVCDecoderConfigurationRecord *hvcc) +{ + unsigned int i, sps_max_sub_layers_minus1, log2_max_pic_order_cnt_lsb_minus4; + unsigned int num_short_term_ref_pic_sets, num_delta_pocs[MAX_SHORT_TERM_RPS_COUNT]; + + skip_bits(gb, 4); // sps_video_parameter_set_id + + sps_max_sub_layers_minus1 = read_bits (gb, 3); + + /* + * numTemporalLayers greater than 1 indicates that the stream to which this + * configuration record applies is temporally scalable and the contained + * number of temporal layers (also referred to as temporal sub-layer or + * sub-layer in ISO/IEC 23008-2) is equal to numTemporalLayers. Value 1 + * indicates that the stream is not temporally scalable. Value 0 indicates + * that it is unknown whether the stream is temporally scalable. + */ + hvcc->numTemporalLayers = MAX(hvcc->numTemporalLayers, + sps_max_sub_layers_minus1 + 1); + + hvcc->temporalIdNested = read_bits1(gb); + + hvcc_parse_ptl(gb, hvcc, sps_max_sub_layers_minus1); + + read_golomb_ue(gb); // sps_seq_parameter_set_id + + hvcc->chromaFormat = read_golomb_ue(gb); + + if (hvcc->chromaFormat == 3) + skip_bits1(gb); // separate_colour_plane_flag + + read_golomb_ue(gb); // pic_width_in_luma_samples + read_golomb_ue(gb); // pic_height_in_luma_samples + + if (read_bits1(gb)) { // conformance_window_flag + read_golomb_ue(gb); // conf_win_left_offset + read_golomb_ue(gb); // conf_win_right_offset + read_golomb_ue(gb); // conf_win_top_offset + read_golomb_ue(gb); // conf_win_bottom_offset + } + + hvcc->bitDepthLumaMinus8 = read_golomb_ue(gb); + hvcc->bitDepthChromaMinus8 = read_golomb_ue(gb); + log2_max_pic_order_cnt_lsb_minus4 = read_golomb_ue(gb); + + /* sps_sub_layer_ordering_info_present_flag */ + i = read_bits1(gb) ? 0 : sps_max_sub_layers_minus1; + for (; i <= sps_max_sub_layers_minus1; i++) + skip_sub_layer_ordering_info(gb); + + read_golomb_ue(gb); // log2_min_luma_coding_block_size_minus3 + read_golomb_ue(gb); // log2_diff_max_min_luma_coding_block_size + read_golomb_ue(gb); // log2_min_transform_block_size_minus2 + read_golomb_ue(gb); // log2_diff_max_min_transform_block_size + read_golomb_ue(gb); // max_transform_hierarchy_depth_inter + read_golomb_ue(gb); // max_transform_hierarchy_depth_intra + + if (read_bits1(gb) && // scaling_list_enabled_flag + read_bits1(gb)) // sps_scaling_list_data_present_flag + skip_scaling_list_data(gb); + + skip_bits1(gb); // amp_enabled_flag + skip_bits1(gb); // sample_adaptive_offset_enabled_flag + + if (read_bits1(gb)) { // pcm_enabled_flag + skip_bits (gb, 4); // pcm_sample_bit_depth_luma_minus1 + skip_bits (gb, 4); // pcm_sample_bit_depth_chroma_minus1 + read_golomb_ue(gb); // log2_min_pcm_luma_coding_block_size_minus3 + read_golomb_ue(gb); // log2_diff_max_min_pcm_luma_coding_block_size + skip_bits1 (gb); // pcm_loop_filter_disabled_flag + } + + num_short_term_ref_pic_sets = read_golomb_ue(gb); + if (num_short_term_ref_pic_sets > MAX_SHORT_TERM_RPS_COUNT) + return -1; + + for (i = 0; i < num_short_term_ref_pic_sets; i++) { + int ret = parse_rps(gb, i, num_short_term_ref_pic_sets, num_delta_pocs); + if (ret < 0) + return ret; + } + + if (read_bits1(gb)) { // long_term_ref_pics_present_flag + unsigned num_long_term_ref_pics_sps = read_golomb_ue(gb); + if (num_long_term_ref_pics_sps > 31U) + return -1; + for (i = 0; i < num_long_term_ref_pics_sps; i++) { // num_long_term_ref_pics_sps + int len = MIN(log2_max_pic_order_cnt_lsb_minus4 + 4, 16); + skip_bits (gb, len); // lt_ref_pic_poc_lsb_sps[i] + skip_bits1(gb); // used_by_curr_pic_lt_sps_flag[i] + } + } + + skip_bits1(gb); // sps_temporal_mvp_enabled_flag + skip_bits1(gb); // strong_intra_smoothing_enabled_flag + + if (read_bits1(gb)) // vui_parameters_present_flag + hvcc_parse_vui(gb, hvcc, sps_max_sub_layers_minus1); + + /* nothing useful for hvcC past this point */ + return 0; +} + +static int hvcc_parse_pps(bitstream_t *gb, + HEVCDecoderConfigurationRecord *hvcc) +{ + uint8_t tiles_enabled_flag, entropy_coding_sync_enabled_flag; + + read_golomb_ue(gb); // pps_pic_parameter_set_id + read_golomb_ue(gb); // pps_seq_parameter_set_id + + /* + * dependent_slice_segments_enabled_flag u(1) + * output_flag_present_flag u(1) + * num_extra_slice_header_bits u(3) + * sign_data_hiding_enabled_flag u(1) + * cabac_init_present_flag u(1) + */ + skip_bits(gb, 7); + + read_golomb_ue(gb); // num_ref_idx_l0_default_active_minus1 + read_golomb_ue(gb); // num_ref_idx_l1_default_active_minus1 + read_golomb_se(gb); // init_qp_minus26 + + /* + * constrained_intra_pred_flag u(1) + * transform_skip_enabled_flag u(1) + */ + skip_bits(gb, 2); + + if (read_bits1(gb)) // cu_qp_delta_enabled_flag + read_golomb_ue(gb); // diff_cu_qp_delta_depth + + read_golomb_se(gb); // pps_cb_qp_offset + read_golomb_se(gb); // pps_cr_qp_offset + + /* + * pps_slice_chroma_qp_offsets_present_flag u(1) + * weighted_pred_flag u(1) + * weighted_bipred_flag u(1) + * transquant_bypass_enabled_flag u(1) + */ + skip_bits(gb, 4); + + tiles_enabled_flag = read_bits1(gb); + entropy_coding_sync_enabled_flag = read_bits1(gb); + + if (entropy_coding_sync_enabled_flag && tiles_enabled_flag) + hvcc->parallelismType = 0; // mixed-type parallel decoding + else if (entropy_coding_sync_enabled_flag) + hvcc->parallelismType = 3; // wavefront-based parallel decoding + else if (tiles_enabled_flag) + hvcc->parallelismType = 2; // tile-based parallel decoding + else + hvcc->parallelismType = 1; // slice-based parallel decoding + + /* nothing useful for hvcC past this point */ + return 0; +} + +static uint8_t *nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len, + uint32_t *dst_len) +{ + uint8_t *dst; + uint32_t i, len; + + dst = malloc(src_len); + if (!dst) + return NULL; + + /* NAL unit header (2 bytes) */ + i = len = 0; + while (i < 2 && i < src_len) + dst[len++] = src[i++]; + + while (i + 2 < src_len) + if (!src[i] && !src[i + 1] && src[i + 2] == 3) { + dst[len++] = src[i++]; + dst[len++] = src[i++]; + i++; // remove emulation_prevention_three_byte + } else + dst[len++] = src[i++]; + + while (i < src_len) + dst[len++] = src[i++]; + + *dst_len = len; + return dst; +} + + + +static void nal_unit_parse_header(bitstream_t *gb, uint8_t *nal_type) +{ + skip_bits1(gb); // forbidden_zero_bit + + *nal_type = read_bits(gb, 6); + + /* + * nuh_layer_id u(6) + * nuh_temporal_id_plus1 u(3) + */ + skip_bits(gb, 9); +} + +static int hvcc_array_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, + uint8_t nal_type, + HEVCDecoderConfigurationRecord *hvcc) +{ + uint8_t index; + uint16_t numNalus; + HVCCNALUnitArray *array; + void *n; + + for (index = 0; index < hvcc->numOfArrays; index++) + if (hvcc->array[index].NAL_unit_type == nal_type) + break; + + if (index >= hvcc->numOfArrays) { + uint8_t i; + + n = realloc(hvcc->array, (index + 1) * sizeof(HVCCNALUnitArray)); + if (n == NULL) + return -1; + hvcc->array = n; + + for (i = hvcc->numOfArrays; i <= index; i++) + memset(&hvcc->array[i], 0, sizeof(HVCCNALUnitArray)); + hvcc->numOfArrays = index + 1; + } + + array = &hvcc->array[index]; + numNalus = array->numNalus; + + n = realloc(array->nalUnit, (numNalus + 1) * sizeof(uint8_t*)); + if (n == NULL) + return -1; + array->nalUnit = n; + + n = realloc(array->nalUnitLength, (numNalus + 1) * sizeof(uint16_t)); + if (n == NULL) + return -1; + array->nalUnitLength = n; + + array->nalUnit [numNalus] = nal_buf; + array->nalUnitLength[numNalus] = nal_size; + array->NAL_unit_type = nal_type; + array->numNalus++; + + return 0; +} + +static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size, + HEVCDecoderConfigurationRecord *hvcc) +{ + int ret = 0; + bitstream_t bs; + uint8_t nal_type; + uint8_t *rbsp_buf; + uint32_t rbsp_size; + + rbsp_buf = nal_unit_extract_rbsp(nal_buf, nal_size, &rbsp_size); + if (!rbsp_buf) { + ret = -1; + goto end; + } + + ret = init_rbits(&bs, rbsp_buf, rbsp_size); + if (ret < 0) + goto end; + + nal_unit_parse_header(&bs, &nal_type); + + /* + * Note: only 'declarative' SEI messages are allowed in + * hvcC. Perhaps the SEI playload type should be checked + * and non-declarative SEI messages discarded? + */ + switch (nal_type) { + case NAL_VPS: + case NAL_SPS: + case NAL_PPS: + case NAL_SEI_PREFIX: + case NAL_SEI_SUFFIX: + ret = hvcc_array_add_nal_unit(nal_buf, nal_size, nal_type, hvcc); + if (ret < 0) + goto end; + else if (nal_type == NAL_VPS) + ret = hvcc_parse_vps(&bs, hvcc); + else if (nal_type == NAL_SPS) + ret = hvcc_parse_sps(&bs, hvcc); + else if (nal_type == NAL_PPS) + ret = hvcc_parse_pps(&bs, hvcc); + if (ret < 0) + goto end; + break; + default: + ret = -1; + goto end; + } + +end: + free(rbsp_buf); + return ret; +} + +static void hvcc_init(HEVCDecoderConfigurationRecord *hvcc) +{ + memset(hvcc, 0, sizeof(HEVCDecoderConfigurationRecord)); + hvcc->configurationVersion = 1; + hvcc->lengthSizeMinusOne = 3; // 4 bytes + + /* + * The following fields have all their valid bits set by default, + * the ProfileTierLevel parsing code will unset them when needed. + */ + hvcc->general_profile_compatibility_flags = 0xffffffff; + hvcc->general_constraint_indicator_flags = 0xffffffffffff; + + /* + * Initialize this field with an invalid value which can be used to detect + * whether we didn't see any VUI (in which case it should be reset to zero). + */ + hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1; +} + +static void hvcc_close(HEVCDecoderConfigurationRecord *hvcc) +{ + uint8_t i; + + for (i = 0; i < hvcc->numOfArrays; i++) { + hvcc->array[i].numNalus = 0; + free(&hvcc->array[i].nalUnit); + hvcc->array[i].nalUnit = NULL; + free(&hvcc->array[i].nalUnitLength); + hvcc->array[i].nalUnitLength = NULL; + } + + hvcc->numOfArrays = 0; + free(hvcc->array); + hvcc->array = NULL; +} + +static int hvcc_write(sbuf_t *pb, HEVCDecoderConfigurationRecord *hvcc) +{ + uint8_t i; + uint16_t j, vps_count = 0, sps_count = 0, pps_count = 0; + + /* + * We only support writing HEVCDecoderConfigurationRecord version 1. + */ + hvcc->configurationVersion = 1; + + /* + * If min_spatial_segmentation_idc is invalid, reset to 0 (unspecified). + */ + if (hvcc->min_spatial_segmentation_idc > MAX_SPATIAL_SEGMENTATION) + hvcc->min_spatial_segmentation_idc = 0; + + /* + * parallelismType indicates the type of parallelism that is used to meet + * the restrictions imposed by min_spatial_segmentation_idc when the value + * of min_spatial_segmentation_idc is greater than 0. + */ + if (!hvcc->min_spatial_segmentation_idc) + hvcc->parallelismType = 0; + + /* + * It's unclear how to properly compute these fields, so + * let's always set them to values meaning 'unspecified'. + */ + hvcc->avgFrameRate = 0; + hvcc->constantFrameRate = 0; + + tvhtrace("hevc", "configurationVersion: %"PRIu8"\n", + hvcc->configurationVersion); + tvhtrace("hevc", "general_profile_space: %"PRIu8"\n", + hvcc->general_profile_space); + tvhtrace("hevc", "general_tier_flag: %"PRIu8"\n", + hvcc->general_tier_flag); + tvhtrace("hevc", "general_profile_idc: %"PRIu8"\n", + hvcc->general_profile_idc); + tvhtrace("hevc", "general_profile_compatibility_flags: 0x%08"PRIx32"\n", + hvcc->general_profile_compatibility_flags); + tvhtrace("hevc", "general_constraint_indicator_flags: 0x%012"PRIx64"\n", + hvcc->general_constraint_indicator_flags); + tvhtrace("hevc", "general_level_idc: %"PRIu8"\n", + hvcc->general_level_idc); + tvhtrace("hevc", "min_spatial_segmentation_idc: %"PRIu16"\n", + hvcc->min_spatial_segmentation_idc); + tvhtrace("hevc", "parallelismType: %"PRIu8"\n", + hvcc->parallelismType); + tvhtrace("hevc", "chromaFormat: %"PRIu8"\n", + hvcc->chromaFormat); + tvhtrace("hevc", "bitDepthLumaMinus8: %"PRIu8"\n", + hvcc->bitDepthLumaMinus8); + tvhtrace("hevc", "bitDepthChromaMinus8: %"PRIu8"\n", + hvcc->bitDepthChromaMinus8); + tvhtrace("hevc", "avgFrameRate: %"PRIu16"\n", + hvcc->avgFrameRate); + tvhtrace("hevc", "constantFrameRate: %"PRIu8"\n", + hvcc->constantFrameRate); + tvhtrace("hevc", "numTemporalLayers: %"PRIu8"\n", + hvcc->numTemporalLayers); + tvhtrace("hevc", "temporalIdNested: %"PRIu8"\n", + hvcc->temporalIdNested); + tvhtrace("hevc", "lengthSizeMinusOne: %"PRIu8"\n", + hvcc->lengthSizeMinusOne); + tvhtrace("hevc", "numOfArrays: %"PRIu8"\n", + hvcc->numOfArrays); + for (i = 0; i < hvcc->numOfArrays; i++) { + tvhtrace("hevc", "NAL_unit_type[%"PRIu8"]: %"PRIu8"\n", + i, hvcc->array[i].NAL_unit_type); + tvhtrace("hevc", "numNalus[%"PRIu8"]: %"PRIu16"\n", + i, hvcc->array[i].numNalus); + for (j = 0; j < hvcc->array[i].numNalus; j++) + tvhtrace("hevc", + "nalUnitLength[%"PRIu8"][%"PRIu16"]: %"PRIu16"\n", + i, j, hvcc->array[i].nalUnitLength[j]); + } + + /* + * We need at least one of each: VPS, SPS and PPS. + */ + for (i = 0; i < hvcc->numOfArrays; i++) + switch (hvcc->array[i].NAL_unit_type) { + case NAL_VPS: + vps_count += hvcc->array[i].numNalus; + break; + case NAL_SPS: + sps_count += hvcc->array[i].numNalus; + break; + case NAL_PPS: + pps_count += hvcc->array[i].numNalus; + break; + default: + break; + } + if (!vps_count || vps_count > MAX_VPS_COUNT || + !sps_count || sps_count > MAX_SPS_COUNT || + !pps_count || pps_count > MAX_PPS_COUNT) + return -1; + + /* unsigned int(8) configurationVersion = 1; */ + sbuf_put_byte(pb, hvcc->configurationVersion); + + /* + * unsigned int(2) general_profile_space; + * unsigned int(1) general_tier_flag; + * unsigned int(5) general_profile_idc; + */ + sbuf_put_byte(pb, hvcc->general_profile_space << 6 | + hvcc->general_tier_flag << 5 | + hvcc->general_profile_idc); + + /* unsigned int(32) general_profile_compatibility_flags; */ + sbuf_put_be32(pb, hvcc->general_profile_compatibility_flags); + + /* unsigned int(48) general_constraint_indicator_flags; */ + sbuf_put_be32(pb, hvcc->general_constraint_indicator_flags >> 16); + sbuf_put_be16(pb, hvcc->general_constraint_indicator_flags); + + /* unsigned int(8) general_level_idc; */ + sbuf_put_byte(pb, hvcc->general_level_idc); + + /* + * bit(4) reserved = ‘1111’b; + * unsigned int(12) min_spatial_segmentation_idc; + */ + sbuf_put_be16(pb, hvcc->min_spatial_segmentation_idc | 0xf000); + + /* + * bit(6) reserved = ‘111111’b; + * unsigned int(2) parallelismType; + */ + sbuf_put_byte(pb, hvcc->parallelismType | 0xfc); + + /* + * bit(6) reserved = ‘111111’b; + * unsigned int(2) chromaFormat; + */ + sbuf_put_byte(pb, hvcc->chromaFormat | 0xfc); + + /* + * bit(5) reserved = ‘11111’b; + * unsigned int(3) bitDepthLumaMinus8; + */ + sbuf_put_byte(pb, hvcc->bitDepthLumaMinus8 | 0xf8); + + /* + * bit(5) reserved = ‘11111’b; + * unsigned int(3) bitDepthChromaMinus8; + */ + sbuf_put_byte(pb, hvcc->bitDepthChromaMinus8 | 0xf8); + + /* bit(16) avgFrameRate; */ + sbuf_put_be16(pb, hvcc->avgFrameRate); + + /* + * bit(2) constantFrameRate; + * bit(3) numTemporalLayers; + * bit(1) temporalIdNested; + * unsigned int(2) lengthSizeMinusOne; + */ + sbuf_put_byte(pb, hvcc->constantFrameRate << 6 | + hvcc->numTemporalLayers << 3 | + hvcc->temporalIdNested << 2 | + hvcc->lengthSizeMinusOne); + + /* unsigned int(8) numOfArrays; */ + sbuf_put_byte(pb, hvcc->numOfArrays); + + for (i = 0; i < hvcc->numOfArrays; i++) { + /* + * bit(1) array_completeness; + * unsigned int(1) reserved = 0; + * unsigned int(6) NAL_unit_type; + */ + sbuf_put_byte(pb, (0 << 7) | + (hvcc->array[i].NAL_unit_type & 0x3f)); + + /* unsigned int(16) numNalus; */ + sbuf_put_be16(pb, hvcc->array[i].numNalus); + + for (j = 0; j < hvcc->array[i].numNalus; j++) { + /* unsigned int(16) nalUnitLength; */ + sbuf_put_be16(pb, hvcc->array[i].nalUnitLength[j]); + + /* bit(8*nalUnitLength) nalUnit; */ + sbuf_append(pb, hvcc->array[i].nalUnit[j], + hvcc->array[i].nalUnitLength[j]); + } + } + + return 0; +} + +#if 0 +int ff_hevc_annexb2mp4(AVIOContext *pb, const uint8_t *buf_in, + int size, int filter_ps, int *ps_count) +{ + int num_ps = 0, ret = 0; + uint8_t *buf, *end, *start = NULL; + + if (!filter_ps) { + ret = ff_avc_parse_nal_units(pb, buf_in, size); + goto end; + } + + ret = ff_avc_parse_nal_units_buf(buf_in, &start, &size); + if (ret < 0) + goto end; + + ret = 0; + buf = start; + end = start + size; + + while (end - buf > 4) { + uint32_t len = MIN(RB32(buf), end - buf - 4); + uint8_t type = (buf[4] >> 1) & 0x3f; + + buf += 4; + + switch (type) { + case NAL_VPS: + case NAL_SPS: + case NAL_PPS: + num_ps++; + break; + default: + ret += 4 + len; + sbuf_put_be32(pb, len); + sbuf_append(pb, buf, len); + break; + } + + buf += len; + } + +end: + av_free(start); + if (ps_count) + *ps_count = num_ps; + return ret; +} + +int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out, + int *size, int filter_ps, int *ps_count) +{ + AVIOContext *pb; + int ret; + + ret = avio_open_dyn_buf(&pb); + if (ret < 0) + return ret; + + ret = ff_hevc_annexb2mp4(pb, buf_in, *size, filter_ps, ps_count); + *size = avio_close_dyn_buf(pb, buf_out); + + return ret; +} +#endif + +int isom_write_hvcc(sbuf_t *pb, const uint8_t *data, int size) +{ + int ret = 0; + uint8_t *buf, *end, *start = NULL; + HEVCDecoderConfigurationRecord hvcc; + + hvcc_init(&hvcc); + + if (size < 6) { + /* We can't write a valid hvcC from the provided data */ + ret = -1; + goto end; + } else if (*data == 1) { + /* Data is already hvcC-formatted */ + sbuf_append(pb, data, size); + goto end; + } else if (!(RB24(data) == 1 || RB32(data) == 1)) { + /* Not a valid Annex B start code prefix */ + ret = -1; + goto end; + } + + ret = avc_parse_nal_units_buf(data, &start, &size); + if (ret < 0) + goto end; + + buf = start; + end = start + size; + + while (end - buf > 4) { + uint32_t len = MIN(RB32(buf), end - buf - 4); + uint8_t type = (buf[4] >> 1) & 0x3f; + + buf += 4; + + switch (type) { + case NAL_VPS: + case NAL_SPS: + case NAL_PPS: + case NAL_SEI_PREFIX: + case NAL_SEI_SUFFIX: + ret = hvcc_add_nal_unit(buf, len, &hvcc); + if (ret < 0) + goto end; + break; + default: + break; + } + + buf += len; + } + + ret = hvcc_write(pb, &hvcc); + +end: + hvcc_close(&hvcc); + free(start); + return ret; +} + +th_pkt_t * +hevc_convert_pkt(th_pkt_t *src) +{ + sbuf_t payload; + th_pkt_t *pkt = malloc(sizeof(*pkt)); + + *pkt = *src; + pkt->pkt_refcount = 1; + pkt->pkt_meta = NULL; + + sbuf_init(&payload); + + avc_parse_nal_units(&payload, pktbuf_ptr(src->pkt_payload), + pktbuf_len(src->pkt_payload)); + + pkt->pkt_payload = pktbuf_make(payload.sb_data, payload.sb_ptr); + return pkt; +} diff --git a/src/parsers/parser_hevc.h b/src/parsers/parser_hevc.h new file mode 100644 index 000000000..aae9e8aa0 --- /dev/null +++ b/src/parsers/parser_hevc.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 Tim Walker + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PARSER_HEVC_H +#define PARSER_HEVC_H + +#include "tvheadend.h" +#include "packet.h" + +int isom_write_hvcc(sbuf_t *pb, const uint8_t *src, int size); + +th_pkt_t * hevc_convert_pkt(th_pkt_t *src); + +#endif /* PARSER_HEVC_H */ diff --git a/src/plumbing/transcoding.c b/src/plumbing/transcoding.c index f5c839b13..d4af38780 100644 --- a/src/plumbing/transcoding.c +++ b/src/plumbing/transcoding.c @@ -858,13 +858,6 @@ scleanup: /** * Parse MPEG2 header, simplifier version (we know what ffmpeg/libav generates */ -static inline uint32_t -RB32(const uint8_t *d) -{ - return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; -} - - static void extract_mpeg2_global_data(th_pkt_t *n, uint8_t *data, int len) {