summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/BitstreamConverter.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/utils/BitstreamConverter.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/utils/BitstreamConverter.cpp')
-rw-r--r--xbmc/utils/BitstreamConverter.cpp1237
1 files changed, 1237 insertions, 0 deletions
diff --git a/xbmc/utils/BitstreamConverter.cpp b/xbmc/utils/BitstreamConverter.cpp
new file mode 100644
index 0000000..f2224a5
--- /dev/null
+++ b/xbmc/utils/BitstreamConverter.cpp
@@ -0,0 +1,1237 @@
+/*
+ * Copyright (C) 2010-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "utils/log.h"
+
+#include <assert.h>
+
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+
+#include "BitstreamConverter.h"
+#include "BitstreamReader.h"
+#include "BitstreamWriter.h"
+
+#include <algorithm>
+
+enum {
+ AVC_NAL_SLICE=1,
+ AVC_NAL_DPA,
+ AVC_NAL_DPB,
+ AVC_NAL_DPC,
+ AVC_NAL_IDR_SLICE,
+ AVC_NAL_SEI,
+ AVC_NAL_SPS,
+ AVC_NAL_PPS,
+ AVC_NAL_AUD,
+ AVC_NAL_END_SEQUENCE,
+ AVC_NAL_END_STREAM,
+ AVC_NAL_FILLER_DATA,
+ AVC_NAL_SPS_EXT,
+ AVC_NAL_AUXILIARY_SLICE=19
+};
+
+enum
+{
+ HEVC_NAL_TRAIL_N = 0,
+ HEVC_NAL_TRAIL_R = 1,
+ HEVC_NAL_TSA_N = 2,
+ HEVC_NAL_TSA_R = 3,
+ HEVC_NAL_STSA_N = 4,
+ HEVC_NAL_STSA_R = 5,
+ HEVC_NAL_RADL_N = 6,
+ HEVC_NAL_RADL_R = 7,
+ HEVC_NAL_RASL_N = 8,
+ HEVC_NAL_RASL_R = 9,
+ HEVC_NAL_BLA_W_LP = 16,
+ HEVC_NAL_BLA_W_RADL = 17,
+ HEVC_NAL_BLA_N_LP = 18,
+ HEVC_NAL_IDR_W_RADL = 19,
+ HEVC_NAL_IDR_N_LP = 20,
+ HEVC_NAL_CRA_NUT = 21,
+ HEVC_NAL_VPS = 32,
+ HEVC_NAL_SPS = 33,
+ HEVC_NAL_PPS = 34,
+ HEVC_NAL_AUD = 35,
+ HEVC_NAL_EOS_NUT = 36,
+ HEVC_NAL_EOB_NUT = 37,
+ HEVC_NAL_FD_NUT = 38,
+ HEVC_NAL_SEI_PREFIX = 39,
+ HEVC_NAL_SEI_SUFFIX = 40,
+ HEVC_NAL_UNSPEC62 = 62, // Dolby Vision RPU
+ HEVC_NAL_UNSPEC63 = 63 // Dolby Vision EL
+};
+
+enum {
+ SEI_BUFFERING_PERIOD = 0,
+ SEI_PIC_TIMING,
+ SEI_PAN_SCAN_RECT,
+ SEI_FILLER_PAYLOAD,
+ SEI_USER_DATA_REGISTERED_ITU_T_T35,
+ SEI_USER_DATA_UNREGISTERED,
+ SEI_RECOVERY_POINT,
+ SEI_DEC_REF_PIC_MARKING_REPETITION,
+ SEI_SPARE_PIC,
+ SEI_SCENE_INFO,
+ SEI_SUB_SEQ_INFO,
+ SEI_SUB_SEQ_LAYER_CHARACTERISTICS,
+ SEI_SUB_SEQ_CHARACTERISTICS,
+ SEI_FULL_FRAME_FREEZE,
+ SEI_FULL_FRAME_FREEZE_RELEASE,
+ SEI_FULL_FRAME_SNAPSHOT,
+ SEI_PROGRESSIVE_REFINEMENT_SEGMENT_START,
+ SEI_PROGRESSIVE_REFINEMENT_SEGMENT_END,
+ SEI_MOTION_CONSTRAINED_SLICE_GROUP_SET,
+ SEI_FILM_GRAIN_CHARACTERISTICS,
+ SEI_DEBLOCKING_FILTER_DISPLAY_PREFERENCE,
+ SEI_STEREO_VIDEO_INFO,
+ SEI_POST_FILTER_HINTS,
+ SEI_TONE_MAPPING
+};
+
+/*
+ * GStreamer h264 parser
+ * Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+ * (C) 2008 Wim Taymans <wim.taymans@gmail.com>
+ * gsth264parse.c
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * See LICENSES/README.md for more information.
+ */
+static void nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size)
+{
+ bs->data = data;
+ bs->end = data + size;
+ bs->head = 0;
+ // fill with something other than 0 to detect
+ // emulation prevention bytes
+ bs->cache = 0xffffffff;
+}
+
+static uint32_t nal_bs_read(nal_bitstream *bs, int n)
+{
+ uint32_t res = 0;
+ int shift;
+
+ if (n == 0)
+ return res;
+
+ // fill up the cache if we need to
+ while (bs->head < n)
+ {
+ uint8_t a_byte;
+ bool check_three_byte;
+
+ check_three_byte = true;
+next_byte:
+ if (bs->data >= bs->end)
+ {
+ // we're at the end, can't produce more than head number of bits
+ n = bs->head;
+ break;
+ }
+ // get the byte, this can be an emulation_prevention_three_byte that we need
+ // to ignore.
+ a_byte = *bs->data++;
+ if (check_three_byte && a_byte == 0x03 && ((bs->cache & 0xffff) == 0))
+ {
+ // next byte goes unconditionally to the cache, even if it's 0x03
+ check_three_byte = false;
+ goto next_byte;
+ }
+ // shift bytes in cache, moving the head bits of the cache left
+ bs->cache = (bs->cache << 8) | a_byte;
+ bs->head += 8;
+ }
+
+ // bring the required bits down and truncate
+ if ((shift = bs->head - n) > 0)
+ res = static_cast<uint32_t>(bs->cache >> shift);
+ else
+ res = static_cast<uint32_t>(bs->cache);
+
+ // mask out required bits
+ if (n < 32)
+ res &= (1 << n) - 1;
+ bs->head = shift;
+
+ return res;
+}
+
+static bool nal_bs_eos(nal_bitstream *bs)
+{
+ return (bs->data >= bs->end) && (bs->head == 0);
+}
+
+// read unsigned Exp-Golomb code
+static int nal_bs_read_ue(nal_bitstream *bs)
+{
+ int i = 0;
+
+ while (nal_bs_read(bs, 1) == 0 && !nal_bs_eos(bs) && i < 31)
+ i++;
+
+ return ((1 << i) - 1 + nal_bs_read(bs, i));
+}
+
+static const uint8_t* avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *a = p + 4 - ((intptr_t)p & 3);
+
+ for (end -= 3; p < a && p < end; p++)
+ {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ for (end -= 3; p < end; p += 4)
+ {
+ uint32_t x = *(const uint32_t*)p;
+ if ((x - 0x01010101) & (~x) & 0x80808080) // generic
+ {
+ if (p[1] == 0)
+ {
+ if (p[0] == 0 && p[2] == 1)
+ return p;
+ if (p[2] == 0 && p[3] == 1)
+ return p+1;
+ }
+ if (p[3] == 0)
+ {
+ if (p[2] == 0 && p[4] == 1)
+ return p+2;
+ if (p[4] == 0 && p[5] == 1)
+ return p+3;
+ }
+ }
+ }
+
+ for (end += 3; p < end; p++)
+ {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ return end + 3;
+}
+
+static const uint8_t* avc_find_startcode(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *out = avc_find_startcode_internal(p, end);
+ if (p<out && out<end && !out[-1])
+ out--;
+ return out;
+}
+
+static bool has_sei_recovery_point(const uint8_t *p, const uint8_t *end)
+{
+ int pt(0), ps(0), offset(1);
+
+ do
+ {
+ pt = 0;
+ do {
+ pt += p[offset];
+ } while (p[offset++] == 0xFF);
+
+ ps = 0;
+ do {
+ ps += p[offset];
+ } while (p[offset++] == 0xFF);
+
+ if (pt == SEI_RECOVERY_POINT)
+ {
+ nal_bitstream bs;
+ nal_bs_init(&bs, p + offset, ps);
+ return nal_bs_read_ue(&bs) >= 0;
+ }
+ offset += ps;
+ } while (p + offset < end && p[offset] != 0x80);
+
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+CBitstreamParser::CBitstreamParser() = default;
+
+void CBitstreamParser::Close()
+{
+}
+
+bool CBitstreamParser::CanStartDecode(const uint8_t *buf, int buf_size)
+{
+ if (!buf)
+ return false;
+
+ bool rtn = false;
+ uint32_t state = -1;
+ const uint8_t *buf_begin, *buf_end = buf + buf_size;
+
+ for (; rtn == false;)
+ {
+ buf = find_start_code(buf, buf_end, &state);
+ if (buf >= buf_end)
+ {
+ break;
+ }
+
+ switch (state & 0x1f)
+ {
+ case AVC_NAL_SLICE:
+ break;
+ case AVC_NAL_IDR_SLICE:
+ rtn = true;
+ break;
+ case AVC_NAL_SEI:
+ buf_begin = buf - 1;
+ buf = find_start_code(buf, buf_end, &state) - 4;
+ if (has_sei_recovery_point(buf_begin, buf))
+ rtn = true;
+ break;
+ case AVC_NAL_SPS:
+ rtn = true;
+ break;
+ case AVC_NAL_PPS:
+ break;
+ default:
+ break;
+ }
+ }
+
+ return rtn;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+CBitstreamConverter::CBitstreamConverter()
+{
+ m_convert_bitstream = false;
+ m_convertBuffer = NULL;
+ m_convertSize = 0;
+ m_inputBuffer = NULL;
+ m_inputSize = 0;
+ m_to_annexb = false;
+ m_extradata = NULL;
+ m_extrasize = 0;
+ m_convert_3byteTo4byteNALSize = false;
+ m_convert_bytestream = false;
+ m_sps_pps_context.sps_pps_data = NULL;
+ m_start_decode = true;
+}
+
+CBitstreamConverter::~CBitstreamConverter()
+{
+ Close();
+}
+
+bool CBitstreamConverter::Open(enum AVCodecID codec, uint8_t *in_extradata, int in_extrasize, bool to_annexb)
+{
+ m_to_annexb = to_annexb;
+
+ m_codec = codec;
+ switch(m_codec)
+ {
+ case AV_CODEC_ID_H264:
+ if (in_extrasize < 7 || in_extradata == NULL)
+ {
+ CLog::Log(LOGERROR, "CBitstreamConverter::Open avcC data too small or missing");
+ return false;
+ }
+ // valid avcC data (bitstream) always starts with the value 1 (version)
+ if(m_to_annexb)
+ {
+ if ( in_extradata[0] == 1 )
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open bitstream to annexb init");
+ m_extrasize = in_extrasize;
+ m_extradata = (uint8_t*)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_convert_bitstream = BitstreamConvertInitAVC(m_extradata, m_extrasize);
+ return true;
+ }
+ else
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open Invalid avcC");
+ }
+ else
+ {
+ // valid avcC atom data always starts with the value 1 (version)
+ if ( in_extradata[0] != 1 )
+ {
+ if ( (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 0 && in_extradata[3] == 1) ||
+ (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 1) )
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init");
+ // video content is from x264 or from bytestream h264 (AnnexB format)
+ // NAL reformatting to bitstream format needed
+ AVIOContext *pb;
+ if (avio_open_dyn_buf(&pb) < 0)
+ return false;
+ m_convert_bytestream = true;
+ // create a valid avcC atom data from ffmpeg's extradata
+ isom_write_avcc(pb, in_extradata, in_extrasize);
+ // unhook from ffmpeg's extradata
+ in_extradata = NULL;
+ // extract the avcC atom data into extradata then write it into avcCData for VDADecoder
+ in_extrasize = avio_close_dyn_buf(pb, &in_extradata);
+ // make a copy of extradata contents
+ m_extradata = (uint8_t *)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ // done with the converted extradata, we MUST free using av_free
+ av_free(in_extradata);
+ return true;
+ }
+ else
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open invalid avcC atom data");
+ return false;
+ }
+ }
+ else
+ {
+ if (in_extradata[4] == 0xFE)
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init 3 byte to 4 byte nal");
+ // video content is from so silly encoder that think 3 byte NAL sizes
+ // are valid, setup to convert 3 byte NAL sizes to 4 byte.
+ in_extradata[4] = 0xFF;
+ m_convert_3byteTo4byteNALSize = true;
+
+ m_extradata = (uint8_t *)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ return true;
+ }
+ }
+ // valid avcC atom
+ m_extradata = (uint8_t*)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ return true;
+ }
+ return false;
+ break;
+ case AV_CODEC_ID_HEVC:
+ if (in_extrasize < 23 || in_extradata == NULL)
+ {
+ CLog::Log(LOGERROR, "CBitstreamConverter::Open hvcC data too small or missing");
+ return false;
+ }
+ // valid hvcC data (bitstream) always starts with the value 1 (version)
+ if(m_to_annexb)
+ {
+ /**
+ * It seems the extradata is encoded as hvcC format.
+ * Temporarily, we support configurationVersion==0 until 14496-15 3rd
+ * is finalized. When finalized, configurationVersion will be 1 and we
+ * can recognize hvcC by checking if extradata[0]==1 or not.
+ */
+
+ if (in_extradata[0] || in_extradata[1] || in_extradata[2] > 1)
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open bitstream to annexb init");
+ m_extrasize = in_extrasize;
+ m_extradata = (uint8_t*)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_convert_bitstream = BitstreamConvertInitHEVC(m_extradata, m_extrasize);
+ return true;
+ }
+ else
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open Invalid hvcC");
+ }
+ else
+ {
+ // valid hvcC atom data always starts with the value 1 (version)
+ if ( in_extradata[0] != 1 )
+ {
+ if ( (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 0 && in_extradata[3] == 1) ||
+ (in_extradata[0] == 0 && in_extradata[1] == 0 && in_extradata[2] == 1) )
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init");
+ //! @todo convert annexb to bitstream format
+ return false;
+ }
+ else
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open invalid hvcC atom data");
+ return false;
+ }
+ }
+ else
+ {
+ if ((in_extradata[4] & 0x3) == 2)
+ {
+ CLog::Log(LOGINFO, "CBitstreamConverter::Open annexb to bitstream init 3 byte to 4 byte nal");
+ // video content is from so silly encoder that think 3 byte NAL sizes
+ // are valid, setup to convert 3 byte NAL sizes to 4 byte.
+ in_extradata[4] |= 0x03;
+ m_convert_3byteTo4byteNALSize = true;
+ }
+ }
+ // valid hvcC atom
+ m_extradata = (uint8_t*)av_malloc(in_extrasize);
+ memcpy(m_extradata, in_extradata, in_extrasize);
+ m_extrasize = in_extrasize;
+ return true;
+ }
+ return false;
+ break;
+ default:
+ return false;
+ break;
+ }
+ return false;
+}
+
+void CBitstreamConverter::Close(void)
+{
+ if (m_sps_pps_context.sps_pps_data)
+ av_free(m_sps_pps_context.sps_pps_data), m_sps_pps_context.sps_pps_data = NULL;
+
+ if (m_convertBuffer)
+ av_free(m_convertBuffer), m_convertBuffer = NULL;
+ m_convertSize = 0;
+
+ if (m_extradata)
+ av_free(m_extradata), m_extradata = NULL;
+ m_extrasize = 0;
+
+ m_inputSize = 0;
+ m_inputBuffer = NULL;
+
+ m_convert_bitstream = false;
+ m_convert_bytestream = false;
+ m_convert_3byteTo4byteNALSize = false;
+}
+
+bool CBitstreamConverter::Convert(uint8_t *pData, int iSize)
+{
+ if (m_convertBuffer)
+ {
+ av_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_inputSize = 0;
+ m_convertSize = 0;
+ m_inputBuffer = NULL;
+
+ if (pData)
+ {
+ if (m_codec == AV_CODEC_ID_H264 ||
+ m_codec == AV_CODEC_ID_HEVC)
+ {
+ if (m_to_annexb)
+ {
+ int demuxer_bytes = iSize;
+ uint8_t *demuxer_content = pData;
+
+ if (m_convert_bitstream)
+ {
+ // convert demuxer packet from bitstream to bytestream (AnnexB)
+ int bytestream_size = 0;
+ uint8_t *bytestream_buff = NULL;
+
+ BitstreamConvert(demuxer_content, demuxer_bytes, &bytestream_buff, &bytestream_size);
+ if (bytestream_buff && (bytestream_size > 0))
+ {
+ m_convertSize = bytestream_size;
+ m_convertBuffer = bytestream_buff;
+ return true;
+ }
+ else
+ {
+ m_convertSize = 0;
+ m_convertBuffer = NULL;
+ CLog::Log(LOGERROR, "CBitstreamConverter::Convert: error converting.");
+ return false;
+ }
+ }
+ else
+ {
+ m_inputSize = iSize;
+ m_inputBuffer = pData;
+ return true;
+ }
+ }
+ else
+ {
+ m_inputSize = iSize;
+ m_inputBuffer = pData;
+
+ if (m_convert_bytestream)
+ {
+ if(m_convertBuffer)
+ {
+ av_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_convertSize = 0;
+
+ // convert demuxer packet from bytestream (AnnexB) to bitstream
+ AVIOContext *pb;
+
+ if(avio_open_dyn_buf(&pb) < 0)
+ {
+ return false;
+ }
+ m_convertSize = avc_parse_nal_units(pb, pData, iSize);
+ m_convertSize = avio_close_dyn_buf(pb, &m_convertBuffer);
+ }
+ else if (m_convert_3byteTo4byteNALSize)
+ {
+ if(m_convertBuffer)
+ {
+ av_free(m_convertBuffer);
+ m_convertBuffer = NULL;
+ }
+ m_convertSize = 0;
+
+ // convert demuxer packet from 3 byte NAL sizes to 4 byte
+ AVIOContext *pb;
+ if (avio_open_dyn_buf(&pb) < 0)
+ return false;
+
+ uint32_t nal_size;
+ uint8_t *end = pData + iSize;
+ uint8_t *nal_start = pData;
+ while (nal_start < end)
+ {
+ nal_size = BS_RB24(nal_start);
+ avio_wb32(pb, nal_size);
+ nal_start += 3;
+ avio_write(pb, nal_start, nal_size);
+ nal_start += nal_size;
+ }
+
+ m_convertSize = avio_close_dyn_buf(pb, &m_convertBuffer);
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+uint8_t *CBitstreamConverter::GetConvertBuffer() const
+{
+ if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize) && m_convertBuffer != NULL)
+ return m_convertBuffer;
+ else
+ return m_inputBuffer;
+}
+
+int CBitstreamConverter::GetConvertSize() const
+{
+ if((m_convert_bitstream || m_convert_bytestream || m_convert_3byteTo4byteNALSize) && m_convertBuffer != NULL)
+ return m_convertSize;
+ else
+ return m_inputSize;
+}
+
+uint8_t *CBitstreamConverter::GetExtraData() const
+{
+ if(m_convert_bitstream)
+ return m_sps_pps_context.sps_pps_data;
+ else
+ return m_extradata;
+}
+int CBitstreamConverter::GetExtraSize() const
+{
+ if(m_convert_bitstream)
+ return m_sps_pps_context.size;
+ else
+ return m_extrasize;
+}
+
+void CBitstreamConverter::ResetStartDecode(void)
+{
+ m_start_decode = false;
+}
+
+bool CBitstreamConverter::CanStartDecode() const
+{
+ return m_start_decode;
+}
+
+bool CBitstreamConverter::BitstreamConvertInitAVC(void *in_extradata, int in_extrasize)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
+ // and Licensed GPL 2.1 or greater
+
+ m_sps_pps_size = 0;
+ m_sps_pps_context.sps_pps_data = NULL;
+
+ // nothing to filter
+ if (!in_extradata || in_extrasize < 6)
+ return false;
+
+ uint16_t unit_size;
+ uint32_t total_size = 0;
+ uint8_t *out = NULL, unit_nb, sps_done = 0, sps_seen = 0, pps_seen = 0;
+ const uint8_t *extradata = (uint8_t*)in_extradata + 4;
+ static const uint8_t nalu_header[4] = {0, 0, 0, 1};
+
+ // retrieve length coded size
+ m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1;
+
+ // retrieve sps and pps unit(s)
+ unit_nb = *extradata++ & 0x1f; // number of sps unit(s)
+ if (!unit_nb)
+ {
+ goto pps;
+ }
+ else
+ {
+ sps_seen = 1;
+ }
+
+ while (unit_nb--)
+ {
+ void *tmp;
+
+ unit_size = extradata[0] << 8 | extradata[1];
+ total_size += unit_size + 4;
+
+ if (total_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE ||
+ (extradata + 2 + unit_size) > ((uint8_t*)in_extradata + in_extrasize))
+ {
+ av_free(out);
+ return false;
+ }
+ tmp = av_realloc(out, total_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!tmp)
+ {
+ av_free(out);
+ return false;
+ }
+ out = (uint8_t*)tmp;
+ memcpy(out + total_size - unit_size - 4, nalu_header, 4);
+ memcpy(out + total_size - unit_size, extradata + 2, unit_size);
+ extradata += 2 + unit_size;
+
+pps:
+ if (!unit_nb && !sps_done++)
+ {
+ unit_nb = *extradata++; // number of pps unit(s)
+ if (unit_nb)
+ pps_seen = 1;
+ }
+ }
+
+ if (out)
+ memset(out + total_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ if (!sps_seen)
+ CLog::Log(LOGDEBUG, "SPS NALU missing or invalid. The resulting stream may not play");
+ if (!pps_seen)
+ CLog::Log(LOGDEBUG, "PPS NALU missing or invalid. The resulting stream may not play");
+
+ m_sps_pps_context.sps_pps_data = out;
+ m_sps_pps_context.size = total_size;
+ m_sps_pps_context.first_idr = 1;
+ m_sps_pps_context.idr_sps_pps_seen = 0;
+
+ return true;
+}
+
+bool CBitstreamConverter::BitstreamConvertInitHEVC(void *in_extradata, int in_extrasize)
+{
+ m_sps_pps_size = 0;
+ m_sps_pps_context.sps_pps_data = NULL;
+
+ // nothing to filter
+ if (!in_extradata || in_extrasize < 23)
+ return false;
+
+ uint16_t unit_nb, unit_size;
+ uint32_t total_size = 0;
+ uint8_t *out = NULL, array_nb, nal_type, sps_seen = 0, pps_seen = 0;
+ const uint8_t *extradata = (uint8_t*)in_extradata + 21;
+ static const uint8_t nalu_header[4] = {0, 0, 0, 1};
+
+ // retrieve length coded size
+ m_sps_pps_context.length_size = (*extradata++ & 0x3) + 1;
+
+ array_nb = *extradata++;
+ while (array_nb--)
+ {
+ nal_type = *extradata++ & 0x3f;
+ unit_nb = extradata[0] << 8 | extradata[1];
+ extradata += 2;
+
+ if (nal_type == HEVC_NAL_SPS && unit_nb)
+ {
+ sps_seen = 1;
+ }
+ else if (nal_type == HEVC_NAL_PPS && unit_nb)
+ {
+ pps_seen = 1;
+ }
+ while (unit_nb--)
+ {
+ void *tmp;
+
+ unit_size = extradata[0] << 8 | extradata[1];
+ extradata += 2;
+ if (nal_type != HEVC_NAL_SPS &&
+ nal_type != HEVC_NAL_PPS &&
+ nal_type != HEVC_NAL_VPS)
+ {
+ extradata += unit_size;
+ continue;
+ }
+ total_size += unit_size + 4;
+
+ if (total_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE ||
+ (extradata + unit_size) > ((uint8_t*)in_extradata + in_extrasize))
+ {
+ av_free(out);
+ return false;
+ }
+ tmp = av_realloc(out, total_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!tmp)
+ {
+ av_free(out);
+ return false;
+ }
+ out = (uint8_t*)tmp;
+ memcpy(out + total_size - unit_size - 4, nalu_header, 4);
+ memcpy(out + total_size - unit_size, extradata, unit_size);
+ extradata += unit_size;
+ }
+ }
+
+ if (out)
+ memset(out + total_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ if (!sps_seen)
+ CLog::Log(LOGDEBUG, "SPS NALU missing or invalid. The resulting stream may not play");
+ if (!pps_seen)
+ CLog::Log(LOGDEBUG, "PPS NALU missing or invalid. The resulting stream may not play");
+
+ m_sps_pps_context.sps_pps_data = out;
+ m_sps_pps_context.size = total_size;
+ m_sps_pps_context.first_idr = 1;
+ m_sps_pps_context.idr_sps_pps_seen = 0;
+
+ return true;
+}
+
+bool CBitstreamConverter::IsIDR(uint8_t unit_type)
+{
+ switch (m_codec)
+ {
+ case AV_CODEC_ID_H264:
+ return unit_type == AVC_NAL_IDR_SLICE;
+ case AV_CODEC_ID_HEVC:
+ return unit_type == HEVC_NAL_IDR_W_RADL ||
+ unit_type == HEVC_NAL_IDR_N_LP ||
+ unit_type == HEVC_NAL_CRA_NUT;
+ default:
+ return false;
+ }
+}
+
+bool CBitstreamConverter::IsSlice(uint8_t unit_type)
+{
+ switch (m_codec)
+ {
+ case AV_CODEC_ID_H264:
+ return unit_type == AVC_NAL_SLICE;
+ case AV_CODEC_ID_HEVC:
+ return unit_type == HEVC_NAL_TRAIL_R ||
+ unit_type == HEVC_NAL_TRAIL_N ||
+ unit_type == HEVC_NAL_TSA_N ||
+ unit_type == HEVC_NAL_TSA_R ||
+ unit_type == HEVC_NAL_STSA_N ||
+ unit_type == HEVC_NAL_STSA_R ||
+ unit_type == HEVC_NAL_BLA_W_LP ||
+ unit_type == HEVC_NAL_BLA_W_RADL ||
+ unit_type == HEVC_NAL_BLA_N_LP ||
+ unit_type == HEVC_NAL_CRA_NUT ||
+ unit_type == HEVC_NAL_RADL_N ||
+ unit_type == HEVC_NAL_RADL_R ||
+ unit_type == HEVC_NAL_RASL_N ||
+ unit_type == HEVC_NAL_RASL_R;
+ default:
+ return false;
+ }
+}
+
+bool CBitstreamConverter::BitstreamConvert(uint8_t* pData, int iSize, uint8_t **poutbuf, int *poutbuf_size)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
+ // and Licensed GPL 2.1 or greater
+
+ int i;
+ uint8_t *buf = pData;
+ uint32_t buf_size = iSize;
+ uint8_t unit_type, nal_sps, nal_pps, nal_sei;
+ int32_t nal_size;
+ uint32_t cumul_size = 0;
+ const uint8_t *buf_end = buf + buf_size;
+
+ switch (m_codec)
+ {
+ case AV_CODEC_ID_H264:
+ nal_sps = AVC_NAL_SPS;
+ nal_pps = AVC_NAL_PPS;
+ nal_sei = AVC_NAL_SEI;
+ break;
+ case AV_CODEC_ID_HEVC:
+ nal_sps = HEVC_NAL_SPS;
+ nal_pps = HEVC_NAL_PPS;
+ nal_sei = HEVC_NAL_SEI_PREFIX;
+ break;
+ default:
+ return false;
+ }
+
+ do
+ {
+ if (buf + m_sps_pps_context.length_size > buf_end)
+ goto fail;
+
+ for (nal_size = 0, i = 0; i < m_sps_pps_context.length_size; i++)
+ nal_size = (nal_size << 8) | buf[i];
+
+ buf += m_sps_pps_context.length_size;
+ if (m_codec == AV_CODEC_ID_H264)
+ {
+ unit_type = *buf & 0x1f;
+ }
+ else
+ {
+ unit_type = (*buf >> 1) & 0x3f;
+ }
+
+ if (buf + nal_size > buf_end || nal_size <= 0)
+ goto fail;
+
+ // Don't add sps/pps if the unit already contain them
+ if (m_sps_pps_context.first_idr && (unit_type == nal_sps || unit_type == nal_pps))
+ m_sps_pps_context.idr_sps_pps_seen = 1;
+
+ if (!m_start_decode && (unit_type == nal_sps || IsIDR(unit_type) || (unit_type == nal_sei && has_sei_recovery_point(buf, buf + nal_size))))
+ m_start_decode = true;
+
+ // prepend only to the first access unit of an IDR picture, if no sps/pps already present
+ if (m_sps_pps_context.first_idr && IsIDR(unit_type) && !m_sps_pps_context.idr_sps_pps_seen)
+ {
+ BitstreamAllocAndCopy(poutbuf, poutbuf_size, m_sps_pps_context.sps_pps_data,
+ m_sps_pps_context.size, buf, nal_size, unit_type);
+ m_sps_pps_context.first_idr = 0;
+ }
+ else
+ {
+ BitstreamAllocAndCopy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size, unit_type);
+ if (!m_sps_pps_context.first_idr && IsSlice(unit_type))
+ {
+ m_sps_pps_context.first_idr = 1;
+ m_sps_pps_context.idr_sps_pps_seen = 0;
+ }
+ }
+
+ buf += nal_size;
+ cumul_size += nal_size + m_sps_pps_context.length_size;
+ } while (cumul_size < buf_size);
+
+ return true;
+
+fail:
+ av_free(*poutbuf), *poutbuf = NULL;
+ *poutbuf_size = 0;
+ return false;
+}
+
+void CBitstreamConverter::BitstreamAllocAndCopy(uint8_t** poutbuf,
+ int* poutbuf_size,
+ const uint8_t* sps_pps,
+ uint32_t sps_pps_size,
+ const uint8_t* in,
+ uint32_t in_size,
+ uint8_t nal_type)
+{
+ // based on h264_mp4toannexb_bsf.c (ffmpeg)
+ // which is Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
+ // and Licensed GPL 2.1 or greater
+
+ uint32_t offset = *poutbuf_size;
+ uint8_t nal_header_size = offset ? 3 : 4;
+ void *tmp;
+
+ // According to x265, this type is always encoded with four-sized header
+ // https://bitbucket.org/multicoreware/x265_git/src/4bf31dc15fb6d1f93d12ecf21fad5e695f0db5c0/source/encoder/nal.cpp#lines-100
+ if (nal_type == HEVC_NAL_UNSPEC62)
+ nal_header_size = 4;
+
+ *poutbuf_size += sps_pps_size + in_size + nal_header_size;
+ tmp = av_realloc(*poutbuf, *poutbuf_size);
+ if (!tmp)
+ return;
+ *poutbuf = (uint8_t*)tmp;
+ if (sps_pps)
+ memcpy(*poutbuf + offset, sps_pps, sps_pps_size);
+
+ memcpy(*poutbuf + sps_pps_size + nal_header_size + offset, in, in_size);
+ if (!offset)
+ {
+ BS_WB32(*poutbuf + sps_pps_size, 1);
+ }
+ else if (nal_header_size == 4)
+ {
+ (*poutbuf + offset + sps_pps_size)[0] = 0;
+ (*poutbuf + offset + sps_pps_size)[1] = 0;
+ (*poutbuf + offset + sps_pps_size)[2] = 0;
+ (*poutbuf + offset + sps_pps_size)[3] = 1;
+ }
+ else
+ {
+ (*poutbuf + offset + sps_pps_size)[0] = 0;
+ (*poutbuf + offset + sps_pps_size)[1] = 0;
+ (*poutbuf + offset + sps_pps_size)[2] = 1;
+ }
+}
+
+int CBitstreamConverter::avc_parse_nal_units(AVIOContext *pb, const uint8_t *buf_in, int size)
+{
+ const uint8_t *p = buf_in;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ size = 0;
+ nal_start = avc_find_startcode(p, end);
+
+ for (;;) {
+ while (nal_start < end && !*(nal_start++));
+ if (nal_start == end)
+ break;
+
+ nal_end = avc_find_startcode(nal_start, end);
+ avio_wb32(pb, nal_end - nal_start);
+ avio_write(pb, nal_start, nal_end - nal_start);
+ size += 4 + nal_end - nal_start;
+ nal_start = nal_end;
+ }
+ return size;
+}
+
+int CBitstreamConverter::avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size)
+{
+ AVIOContext *pb;
+ int ret = avio_open_dyn_buf(&pb);
+ if (ret < 0)
+ return ret;
+
+ avc_parse_nal_units(pb, buf_in, *size);
+
+ av_freep(buf);
+ *size = avio_close_dyn_buf(pb, buf);
+ return 0;
+}
+
+int CBitstreamConverter::isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len)
+{
+ // extradata from bytestream h264, convert to avcC atom data for bitstream
+ if (len > 6)
+ {
+ /* check for h264 start code */
+ if (BS_RB32(data) == 0x00000001 || BS_RB24(data) == 0x000001)
+ {
+ uint8_t *buf=NULL, *end, *start;
+ uint32_t sps_size=0, pps_size=0;
+ uint8_t *sps=0, *pps=0;
+
+ int ret = avc_parse_nal_units_buf(data, &buf, &len);
+ if (ret < 0)
+ return ret;
+ start = buf;
+ end = buf + len;
+
+ /* look for sps and pps */
+ while (end - buf > 4)
+ {
+ uint32_t size;
+ uint8_t nal_type;
+ size = std::min<uint32_t>(BS_RB32(buf), end - buf - 4);
+ buf += 4;
+ nal_type = buf[0] & 0x1f;
+ if (nal_type == 7) /* SPS */
+ {
+ sps = buf;
+ sps_size = size;
+ }
+ else if (nal_type == 8) /* PPS */
+ {
+ pps = buf;
+ pps_size = size;
+ }
+ buf += size;
+ }
+ if (!sps || !pps || sps_size < 4 || sps_size > UINT16_MAX || pps_size > UINT16_MAX)
+ assert(0);
+
+ avio_w8(pb, 1); /* version */
+ avio_w8(pb, sps[1]); /* profile */
+ avio_w8(pb, sps[2]); /* profile compat */
+ avio_w8(pb, sps[3]); /* level */
+ avio_w8(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
+ avio_w8(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */
+
+ avio_wb16(pb, sps_size);
+ avio_write(pb, sps, sps_size);
+ if (pps)
+ {
+ avio_w8(pb, 1); /* number of pps */
+ avio_wb16(pb, pps_size);
+ avio_write(pb, pps, pps_size);
+ }
+ av_free(start);
+ }
+ else
+ {
+ avio_write(pb, data, len);
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+bool CBitstreamConverter::mpeg2_sequence_header(const uint8_t *data, const uint32_t size, mpeg2_sequence *sequence)
+{
+ // parse nal's until a sequence_header_code is found
+ // and return the width, height, aspect ratio and frame rate if changed.
+ bool changed = false;
+
+ if (!data)
+ return changed;
+
+ const uint8_t *p = data;
+ const uint8_t *end = p + size;
+ const uint8_t *nal_start, *nal_end;
+
+ nal_start = avc_find_startcode(p, end);
+ while (nal_start < end)
+ {
+ while (!*(nal_start++));
+ nal_end = avc_find_startcode(nal_start, end);
+ if (*nal_start == 0xB3)
+ {
+ nal_bitstream bs;
+ nal_bs_init(&bs, nal_start, end - nal_start);
+
+ // sequence_header_code
+ nal_bs_read(&bs, 8);
+
+ // width
+ // nal_start + 12 bits == horizontal_size_value
+ uint32_t width = nal_bs_read(&bs, 12);
+ if (width != sequence->width)
+ {
+ changed = true;
+ sequence->width = width;
+ }
+ // height
+ // nal_start + 24 bits == vertical_size_value
+ uint32_t height = nal_bs_read(&bs, 12);
+ if (height != sequence->height)
+ {
+ changed = true;
+ sequence->height = height;
+ }
+
+ // aspect ratio
+ // nal_start + 28 bits == aspect_ratio_information
+ float ratio = sequence->ratio;
+ uint32_t ratio_info = nal_bs_read(&bs, 4);
+ switch(ratio_info)
+ {
+ case 0x01:
+ ratio = 1.0f;
+ break;
+ default:
+ case 0x02:
+ ratio = 4.0f/3;
+ break;
+ case 0x03:
+ ratio = 16.0f/9;
+ break;
+ case 0x04:
+ ratio = 2.21f;
+ break;
+ }
+ if (ratio_info != sequence->ratio_info)
+ {
+ changed = true;
+ sequence->ratio = ratio;
+ sequence->ratio_info = ratio_info;
+ }
+
+ // frame rate
+ // nal_start + 32 bits == frame_rate_code
+ uint32_t fpsrate = sequence->fps_rate;
+ uint32_t fpsscale = sequence->fps_scale;
+ uint32_t rate_info = nal_bs_read(&bs, 4);
+
+ switch(rate_info)
+ {
+ default:
+ case 0x01:
+ fpsrate = 24000;
+ fpsscale = 1001;
+ break;
+ case 0x02:
+ fpsrate = 24000;
+ fpsscale = 1000;
+ break;
+ case 0x03:
+ fpsrate = 25000;
+ fpsscale = 1000;
+ break;
+ case 0x04:
+ fpsrate = 30000;
+ fpsscale = 1001;
+ break;
+ case 0x05:
+ fpsrate = 30000;
+ fpsscale = 1000;
+ break;
+ case 0x06:
+ fpsrate = 50000;
+ fpsscale = 1000;
+ break;
+ case 0x07:
+ fpsrate = 60000;
+ fpsscale = 1001;
+ break;
+ case 0x08:
+ fpsrate = 60000;
+ fpsscale = 1000;
+ break;
+ }
+
+ if (fpsscale != sequence->fps_scale || fpsrate != sequence->fps_rate)
+ {
+ changed = true;
+ sequence->fps_rate = fpsrate;
+ sequence->fps_scale = fpsscale;
+ }
+ }
+ nal_start = nal_end;
+ }
+
+ return changed;
+}
+