]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
muxers: mkv/libav add HEVC support
authorJaroslav Kysela <perex@perex.cz>
Fri, 18 Sep 2015 14:11:38 +0000 (16:11 +0200)
committerJaroslav Kysela <perex@perex.cz>
Fri, 18 Sep 2015 18:12:06 +0000 (20:12 +0200)
Makefile
src/muxer/muxer_libav.c
src/muxer/tvh/mkmux.c
src/parsers/bitstream.c
src/parsers/bitstream.h
src/parsers/parser_avc.c
src/parsers/parser_avc.h
src/parsers/parser_hevc.c [new file with mode: 0644]
src/parsers/parser_hevc.h [new file with mode: 0644]
src/plumbing/transcoding.c

index 3cb1a9495a5f40db4a6c5b699cae6a5cd2f43f48..efe05297721b86c5ca128e588ad2b4aa9fbf0549 100644 (file)
--- 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 \
index 7f4cd63d8193cfd436a718ffdacdf2d58ca498e8..4091e0525923ee4cbc97db7fb00f0b5f8eaed837 100644 (file)
@@ -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, 
index 192c95893395426ddedf7c03427dedc9e4fb9f55..3f886cd85be988e8db97568c1a71bc8e53586922 100644 (file)
@@ -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));
index 2ca611d001a499c16fdaf06e5e25f9a2772f6682..9e9fefdb94feec4d4fb8d406aca409ebedab07c5 100644 (file)
 #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)
index f4aecf51477bfbdfcd211e9713b6e0314496fe53..4e4d4b960285eeda2000a675f773dcf876e1e16c 100644 (file)
 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_ */
index 87e764113ed7360db830057d53f31d61138e2619..d23f6d96045efd6a561a818b266b89e8ec64f30f 100644 (file)
@@ -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;
        
index 75d74b671b808b70874ee4f2facd600492d73be2..90618cab289b6a17ef23752be04c184f8ae288cf 100644 (file)
 #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 (file)
index 0000000..11825ba
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com>
+ *
+ * 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 (file)
index 0000000..aae9e8a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Tim Walker <tdskywalker@gmail.com>
+ *
+ * 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 */
index f5c839b13fb3d8e91cc9b997f0bd79ec764942db..d4af38780a418d1bd17964d331caa5ac65a214ae 100644 (file)
@@ -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)
 {