diff --git a/src/h265_parser/gstbitreader.c b/src/h265_parser/gstbitreader.c new file mode 100644 index 0000000..405f2e1 --- /dev/null +++ b/src/h265_parser/gstbitreader.c @@ -0,0 +1,307 @@ +/* GStreamer + * + * Copyright (C) 2008 Sebastian Dröge . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GST_BIT_READER_DISABLE_INLINES +#include "gstbitreader.h" + +#include + +/** + * SECTION:gstbitreader + * @title: GstBitReader + * @short_description: Reads any number of bits from a memory buffer + * @symbols: + * - gst_bit_reader_skip_unchecked + * - gst_bit_reader_skip_to_byte_unchecked + * - gst_bit_reader_get_bits_uint8_unchecked + * - gst_bit_reader_peek_bits_uint8_unchecked + * - gst_bit_reader_get_bits_uint16_unchecked + * - gst_bit_reader_peek_bits_uint16_unchecked + * - gst_bit_reader_get_bits_uint32_unchecked + * - gst_bit_reader_peek_bits_uint32_unchecked + * - gst_bit_reader_get_bits_uint64_unchecked + * - gst_bit_reader_peek_bits_uint64_unchecked + * + * #GstBitReader provides a bit reader that can read any number of bits + * from a memory buffer. It provides functions for reading any number of bits + * into 8, 16, 32 and 64 bit variables. + */ + +/** + * gst_bit_reader_new: (skip) + * @data: (array length=size): Data from which the #GstBitReader + * should read + * @size: Size of @data in bytes + * + * Create a new #GstBitReader instance, which will read from @data. + * + * Free-function: gst_bit_reader_free + * + * Returns: (transfer full): a new #GstBitReader instance + */ +GstBitReader * +gst_bit_reader_new (const guint8 * data, guint size) +{ + GstBitReader *ret = g_new0 (GstBitReader, 1); + + ret->data = data; + ret->size = size; + + return ret; +} + +/** + * gst_bit_reader_free: + * @reader: (in) (transfer full): a #GstBitReader instance + * + * Frees a #GstBitReader instance, which was previously allocated by + * gst_bit_reader_new(). + */ +void +gst_bit_reader_free (GstBitReader * reader) +{ + g_return_if_fail (reader != NULL); + + g_free (reader); +} + +/** + * gst_bit_reader_init: + * @reader: a #GstBitReader instance + * @data: (in) (array length=size): data from which the bit reader should read + * @size: Size of @data in bytes + * + * Initializes a #GstBitReader instance to read from @data. This function + * can be called on already initialized instances. + */ +void +gst_bit_reader_init (GstBitReader * reader, const guint8 * data, guint size) +{ + g_return_if_fail (reader != NULL); + + reader->data = data; + reader->size = size; + reader->byte = reader->bit = 0; +} + +/** + * gst_bit_reader_set_pos: + * @reader: a #GstBitReader instance + * @pos: The new position in bits + * + * Sets the new position of a #GstBitReader instance to @pos in bits. + * + * Returns: %TRUE if the position could be set successfully, %FALSE + * otherwise. + */ +gboolean +gst_bit_reader_set_pos (GstBitReader * reader, guint pos) +{ + g_return_val_if_fail (reader != NULL, FALSE); + + if (pos > reader->size * 8) + return FALSE; + + reader->byte = pos / 8; + reader->bit = pos % 8; + + return TRUE; +} + +/** + * gst_bit_reader_get_pos: + * @reader: a #GstBitReader instance + * + * Returns the current position of a #GstBitReader instance in bits. + * + * Returns: The current position of @reader in bits. + */ +guint +gst_bit_reader_get_pos (const GstBitReader * reader) +{ + return _gst_bit_reader_get_pos_inline (reader); +} + +/** + * gst_bit_reader_get_remaining: + * @reader: a #GstBitReader instance + * + * Returns the remaining number of bits of a #GstBitReader instance. + * + * Returns: The remaining number of bits of @reader instance. + */ +guint +gst_bit_reader_get_remaining (const GstBitReader * reader) +{ + return _gst_bit_reader_get_remaining_inline (reader); +} + +/** + * gst_bit_reader_get_size: + * @reader: a #GstBitReader instance + * + * Returns the total number of bits of a #GstBitReader instance. + * + * Returns: The total number of bits of @reader instance. + */ +guint +gst_bit_reader_get_size (const GstBitReader * reader) +{ + return _gst_bit_reader_get_size_inline (reader); +} + +/** + * gst_bit_reader_skip: + * @reader: a #GstBitReader instance + * @nbits: the number of bits to skip + * + * Skips @nbits bits of the #GstBitReader instance. + * + * Returns: %TRUE if @nbits bits could be skipped, %FALSE otherwise. + */ +gboolean +gst_bit_reader_skip (GstBitReader * reader, guint nbits) +{ + return _gst_bit_reader_skip_inline (reader, nbits); +} + +/** + * gst_bit_reader_skip_to_byte: + * @reader: a #GstBitReader instance + * + * Skips until the next byte. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_bit_reader_skip_to_byte (GstBitReader * reader) +{ + return _gst_bit_reader_skip_to_byte_inline (reader); +} + +/** + * gst_bit_reader_get_bits_uint8: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint8 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_get_bits_uint16: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint16 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_get_bits_uint32: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint32 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_get_bits_uint64: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint64 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_peek_bits_uint8: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint8 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_peek_bits_uint16: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint16 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_peek_bits_uint32: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint32 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_bit_reader_peek_bits_uint64: + * @reader: a #GstBitReader instance + * @val: (out): Pointer to a #guint64 to store the result + * @nbits: number of bits to read + * + * Read @nbits bits into @val but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +#define GST_BIT_READER_READ_BITS(bits) \ +gboolean \ +gst_bit_reader_peek_bits_uint##bits (const GstBitReader *reader, guint##bits *val, guint nbits) \ +{ \ + return _gst_bit_reader_peek_bits_uint##bits##_inline (reader, val, nbits); \ +} \ +\ +gboolean \ +gst_bit_reader_get_bits_uint##bits (GstBitReader *reader, guint##bits *val, guint nbits) \ +{ \ + return _gst_bit_reader_get_bits_uint##bits##_inline (reader, val, nbits); \ +} + +GST_BIT_READER_READ_BITS (8); +GST_BIT_READER_READ_BITS (16); +GST_BIT_READER_READ_BITS (32); +GST_BIT_READER_READ_BITS (64); diff --git a/src/h265_parser/gstbitreader.h b/src/h265_parser/gstbitreader.h new file mode 100644 index 0000000..380edd3 --- /dev/null +++ b/src/h265_parser/gstbitreader.h @@ -0,0 +1,328 @@ +/* GStreamer + * + * Copyright (C) 2008 Sebastian Dröge . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_BIT_READER_H__ +#define __GST_BIT_READER_H__ + +#include +#include + +/* FIXME: inline functions */ + +G_BEGIN_DECLS + +#define GST_BIT_READER(reader) ((GstBitReader *) (reader)) + +/** + * GstBitReader: + * @data: (array length=size): Data from which the bit reader will + * read + * @size: Size of @data in bytes + * @byte: Current byte position + * @bit: Bit position in the current byte + * + * A bit reader instance. + */ +typedef struct { + const guint8 *data; + guint size; + + guint byte; /* Byte position */ + guint bit; /* Bit position in the current byte */ + + /* < private > */ + gpointer _gst_reserved[GST_PADDING]; +} GstBitReader; + +GST_BASE_API +GstBitReader * gst_bit_reader_new (const guint8 *data, guint size) G_GNUC_MALLOC; + +GST_BASE_API +void gst_bit_reader_free (GstBitReader *reader); + +GST_BASE_API +void gst_bit_reader_init (GstBitReader *reader, const guint8 *data, guint size); + +GST_BASE_API +gboolean gst_bit_reader_set_pos (GstBitReader *reader, guint pos); + +GST_BASE_API +guint gst_bit_reader_get_pos (const GstBitReader *reader); + +GST_BASE_API +guint gst_bit_reader_get_remaining (const GstBitReader *reader); + +GST_BASE_API +guint gst_bit_reader_get_size (const GstBitReader *reader); + +GST_BASE_API +gboolean gst_bit_reader_skip (GstBitReader *reader, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_skip_to_byte (GstBitReader *reader); + +GST_BASE_API +gboolean gst_bit_reader_get_bits_uint8 (GstBitReader *reader, guint8 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_get_bits_uint16 (GstBitReader *reader, guint16 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_get_bits_uint32 (GstBitReader *reader, guint32 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_get_bits_uint64 (GstBitReader *reader, guint64 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_peek_bits_uint8 (const GstBitReader *reader, guint8 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_peek_bits_uint16 (const GstBitReader *reader, guint16 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_peek_bits_uint32 (const GstBitReader *reader, guint32 *val, guint nbits); + +GST_BASE_API +gboolean gst_bit_reader_peek_bits_uint64 (const GstBitReader *reader, guint64 *val, guint nbits); + +/** + * GST_BIT_READER_INIT: + * @data: Data from which the #GstBitReader should read + * @size: Size of @data in bytes + * + * A #GstBitReader must be initialized with this macro, before it can be + * used. This macro can used be to initialize a variable, but it cannot + * be assigned to a variable. In that case you have to use + * gst_bit_reader_init(). + */ +#define GST_BIT_READER_INIT(data, size) {data, size, 0, 0} + +/* Unchecked variants */ + +static inline void +gst_bit_reader_skip_unchecked (GstBitReader * reader, guint nbits) +{ + reader->bit += nbits; + reader->byte += reader->bit / 8; + reader->bit = reader->bit % 8; +} + +static inline void +gst_bit_reader_skip_to_byte_unchecked (GstBitReader * reader) +{ + if (reader->bit) { + reader->bit = 0; + reader->byte++; + } +} + +#define __GST_BIT_READER_READ_BITS_UNCHECKED(bits) \ +static inline guint##bits \ +gst_bit_reader_peek_bits_uint##bits##_unchecked (const GstBitReader *reader, guint nbits) \ +{ \ + guint##bits ret = 0; \ + const guint8 *data; \ + guint byte, bit; \ + \ + data = reader->data; \ + byte = reader->byte; \ + bit = reader->bit; \ + \ + while (nbits > 0) { \ + guint toread = MIN (nbits, 8 - bit); \ + \ + ret <<= toread; \ + ret |= (data[byte] & (0xff >> bit)) >> (8 - toread - bit); \ + \ + bit += toread; \ + if (bit >= 8) { \ + byte++; \ + bit = 0; \ + } \ + nbits -= toread; \ + } \ + \ + return ret; \ +} \ +\ +static inline guint##bits \ +gst_bit_reader_get_bits_uint##bits##_unchecked (GstBitReader *reader, guint nbits) \ +{ \ + guint##bits ret; \ + \ + ret = gst_bit_reader_peek_bits_uint##bits##_unchecked (reader, nbits); \ + \ + gst_bit_reader_skip_unchecked (reader, nbits); \ + \ + return ret; \ +} + +__GST_BIT_READER_READ_BITS_UNCHECKED (8) +__GST_BIT_READER_READ_BITS_UNCHECKED (16) +__GST_BIT_READER_READ_BITS_UNCHECKED (32) +__GST_BIT_READER_READ_BITS_UNCHECKED (64) + +#undef __GST_BIT_READER_READ_BITS_UNCHECKED + +/* unchecked variants -- do not use */ + +static inline guint +_gst_bit_reader_get_size_unchecked (const GstBitReader * reader) +{ + return reader->size * 8; +} + +static inline guint +_gst_bit_reader_get_pos_unchecked (const GstBitReader * reader) +{ + return reader->byte * 8 + reader->bit; +} + +static inline guint +_gst_bit_reader_get_remaining_unchecked (const GstBitReader * reader) +{ + return reader->size * 8 - (reader->byte * 8 + reader->bit); +} + +/* inlined variants -- do not use directly */ +static inline guint +_gst_bit_reader_get_size_inline (const GstBitReader * reader) +{ + g_return_val_if_fail (reader != NULL, 0); + + return _gst_bit_reader_get_size_unchecked (reader); +} + +static inline guint +_gst_bit_reader_get_pos_inline (const GstBitReader * reader) +{ + g_return_val_if_fail (reader != NULL, 0); + + return _gst_bit_reader_get_pos_unchecked (reader); +} + +static inline guint +_gst_bit_reader_get_remaining_inline (const GstBitReader * reader) +{ + g_return_val_if_fail (reader != NULL, 0); + + return _gst_bit_reader_get_remaining_unchecked (reader); +} + +static inline gboolean +_gst_bit_reader_skip_inline (GstBitReader * reader, guint nbits) +{ + g_return_val_if_fail (reader != NULL, FALSE); + + if (_gst_bit_reader_get_remaining_unchecked (reader) < nbits) + return FALSE; + + gst_bit_reader_skip_unchecked (reader, nbits); + + return TRUE; +} + +static inline gboolean +_gst_bit_reader_skip_to_byte_inline (GstBitReader * reader) +{ + g_return_val_if_fail (reader != NULL, FALSE); + + if (reader->byte > reader->size) + return FALSE; + + gst_bit_reader_skip_to_byte_unchecked (reader); + + return TRUE; +} + +#define __GST_BIT_READER_READ_BITS_INLINE(bits) \ +static inline gboolean \ +_gst_bit_reader_get_bits_uint##bits##_inline (GstBitReader *reader, guint##bits *val, guint nbits) \ +{ \ + g_return_val_if_fail (reader != NULL, FALSE); \ + g_return_val_if_fail (val != NULL, FALSE); \ + g_return_val_if_fail (nbits <= bits, FALSE); \ + \ + if (_gst_bit_reader_get_remaining_unchecked (reader) < nbits) \ + return FALSE; \ +\ + *val = gst_bit_reader_get_bits_uint##bits##_unchecked (reader, nbits); \ + return TRUE; \ +} \ +\ +static inline gboolean \ +_gst_bit_reader_peek_bits_uint##bits##_inline (const GstBitReader *reader, guint##bits *val, guint nbits) \ +{ \ + g_return_val_if_fail (reader != NULL, FALSE); \ + g_return_val_if_fail (val != NULL, FALSE); \ + g_return_val_if_fail (nbits <= bits, FALSE); \ + \ + if (_gst_bit_reader_get_remaining_unchecked (reader) < nbits) \ + return FALSE; \ +\ + *val = gst_bit_reader_peek_bits_uint##bits##_unchecked (reader, nbits); \ + return TRUE; \ +} + +__GST_BIT_READER_READ_BITS_INLINE (8) +__GST_BIT_READER_READ_BITS_INLINE (16) +__GST_BIT_READER_READ_BITS_INLINE (32) +__GST_BIT_READER_READ_BITS_INLINE (64) + +#undef __GST_BIT_READER_READ_BITS_INLINE + +#ifndef GST_BIT_READER_DISABLE_INLINES + +#define gst_bit_reader_get_size(reader) \ + _gst_bit_reader_get_size_inline (reader) +#define gst_bit_reader_get_pos(reader) \ + _gst_bit_reader_get_pos_inline (reader) +#define gst_bit_reader_get_remaining(reader) \ + _gst_bit_reader_get_remaining_inline (reader) + +/* we use defines here so we can add the G_LIKELY() */ + +#define gst_bit_reader_skip(reader, nbits)\ + G_LIKELY (_gst_bit_reader_skip_inline(reader, nbits)) +#define gst_bit_reader_skip_to_byte(reader)\ + G_LIKELY (_gst_bit_reader_skip_to_byte_inline(reader)) + +#define gst_bit_reader_get_bits_uint8(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_get_bits_uint8_inline (reader, val, nbits)) +#define gst_bit_reader_get_bits_uint16(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_get_bits_uint16_inline (reader, val, nbits)) +#define gst_bit_reader_get_bits_uint32(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_get_bits_uint32_inline (reader, val, nbits)) +#define gst_bit_reader_get_bits_uint64(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_get_bits_uint64_inline (reader, val, nbits)) + +#define gst_bit_reader_peek_bits_uint8(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_peek_bits_uint8_inline (reader, val, nbits)) +#define gst_bit_reader_peek_bits_uint16(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_peek_bits_uint16_inline (reader, val, nbits)) +#define gst_bit_reader_peek_bits_uint32(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_peek_bits_uint32_inline (reader, val, nbits)) +#define gst_bit_reader_peek_bits_uint64(reader, val, nbits) \ + G_LIKELY (_gst_bit_reader_peek_bits_uint64_inline (reader, val, nbits)) +#endif + +G_END_DECLS + +#endif /* __GST_BIT_READER_H__ */ diff --git a/src/h265_parser/gstbytereader.c b/src/h265_parser/gstbytereader.c new file mode 100644 index 0000000..5fee9e1 --- /dev/null +++ b/src/h265_parser/gstbytereader.c @@ -0,0 +1,1301 @@ +/* GStreamer byte reader + * + * Copyright (C) 2008 Sebastian Dröge . + * Copyright (C) 2009,2014 Tim-Philipp Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GST_BYTE_READER_DISABLE_INLINES +#include "gstbytereader.h" + +#include "gst/glib-compat-private.h" +#include + +/** + * SECTION:gstbytereader + * @title: GstByteReader + * @short_description: Reads different integer, string and floating point + * types from a memory buffer + * @symbols: + * - gst_byte_reader_skip_unchecked + * - gst_byte_reader_get_uint8_unchecked + * - gst_byte_reader_peek_uint8_unchecked + * - gst_byte_reader_get_int8_unchecked + * - gst_byte_reader_peek_int8_unchecked + * - gst_byte_reader_get_uint16_le_unchecked + * - gst_byte_reader_get_uint16_be_unchecked + * - gst_byte_reader_peek_uint16_le_unchecked + * - gst_byte_reader_peek_uint16_be_unchecked + * - gst_byte_reader_get_int16_le_unchecked + * - gst_byte_reader_get_int16_be_unchecked + * - gst_byte_reader_peek_int16_le_unchecked + * - gst_byte_reader_peek_int16_be_unchecked + * - gst_byte_reader_get_uint24_le_unchecked + * - gst_byte_reader_get_uint24_be_unchecked + * - gst_byte_reader_peek_uint24_le_unchecked + * - gst_byte_reader_peek_uint24_be_unchecked + * - gst_byte_reader_get_int24_le_unchecked + * - gst_byte_reader_get_int24_be_unchecked + * - gst_byte_reader_peek_int24_le_unchecked + * - gst_byte_reader_peek_int24_be_unchecked + * - gst_byte_reader_get_uint32_le_unchecked + * - gst_byte_reader_get_uint32_be_unchecked + * - gst_byte_reader_peek_uint32_le_unchecked + * - gst_byte_reader_peek_uint32_be_unchecked + * - gst_byte_reader_get_int32_le_unchecked + * - gst_byte_reader_get_int32_be_unchecked + * - gst_byte_reader_peek_int32_le_unchecked + * - gst_byte_reader_peek_int32_be_unchecked + * - gst_byte_reader_get_float32_le_unchecked + * - gst_byte_reader_get_float32_be_unchecked + * - gst_byte_reader_get_float64_le_unchecked + * - gst_byte_reader_get_float64_be_unchecked + * - gst_byte_reader_peek_float32_le_unchecked + * - gst_byte_reader_peek_float32_be_unchecked + * - gst_byte_reader_peek_float64_le_unchecked + * - gst_byte_reader_peek_float64_be_unchecked + * - gst_byte_reader_peek_data_unchecked + * - gst_byte_reader_get_data_unchecked + * - gst_byte_reader_dup_data_unchecked + * + * #GstByteReader provides a byte reader that can read different integer and + * floating point types from a memory buffer. It provides functions for reading + * signed/unsigned, little/big endian integers of 8, 16, 24, 32 and 64 bits + * and functions for reading little/big endian floating points numbers of + * 32 and 64 bits. It also provides functions to read NUL-terminated strings + * in various character encodings. + */ + +/** + * gst_byte_reader_new: (skip) + * @data: (in) (transfer none) (array length=size): data from which the + * #GstByteReader should read + * @size: Size of @data in bytes + * + * Create a new #GstByteReader instance, which will read from @data. + * + * Free-function: gst_byte_reader_free + * + * Returns: (transfer full): a new #GstByteReader instance + */ +GstByteReader * +gst_byte_reader_new (const guint8 * data, guint size) +{ + GstByteReader *ret = g_new0 (GstByteReader, 1); + + ret->data = data; + ret->size = size; + + return ret; +} + +/** + * gst_byte_reader_free: + * @reader: (in) (transfer full): a #GstByteReader instance + * + * Frees a #GstByteReader instance, which was previously allocated by + * gst_byte_reader_new(). + */ +void +gst_byte_reader_free (GstByteReader * reader) +{ + g_return_if_fail (reader != NULL); + + g_free (reader); +} + +/** + * gst_byte_reader_init: + * @reader: a #GstByteReader instance + * @data: (in) (transfer none) (array length=size): data from which + * the #GstByteReader should read + * @size: Size of @data in bytes + * + * Initializes a #GstByteReader instance to read from @data. This function + * can be called on already initialized instances. + */ +void +gst_byte_reader_init (GstByteReader * reader, const guint8 * data, guint size) +{ + g_return_if_fail (reader != NULL); + + reader->data = data; + reader->size = size; + reader->byte = 0; +} + +/** + * gst_byte_reader_peek_sub_reader: (skip) + * @reader: an existing and initialized #GstByteReader instance + * @sub_reader: a #GstByteReader instance to initialize as sub-reader + * @size: size of @sub_reader in bytes + * + * Initializes a #GstByteReader sub-reader instance to contain @size bytes of + * data from the current position of @reader. This is useful to read chunked + * formats and make sure that one doesn't read beyond the size of the sub-chunk. + * + * Unlike gst_byte_reader_get_sub_reader(), this function does not modify the + * current position of @reader. + * + * Returns: FALSE on error or if @reader does not contain @size more bytes from + * the current position, and otherwise TRUE + * + * Since: 1.6 + */ +gboolean +gst_byte_reader_peek_sub_reader (GstByteReader * reader, + GstByteReader * sub_reader, guint size) +{ + return _gst_byte_reader_peek_sub_reader_inline (reader, sub_reader, size); +} + +/** + * gst_byte_reader_get_sub_reader: (skip) + * @reader: an existing and initialized #GstByteReader instance + * @sub_reader: a #GstByteReader instance to initialize as sub-reader + * @size: size of @sub_reader in bytes + * + * Initializes a #GstByteReader sub-reader instance to contain @size bytes of + * data from the current position of @reader. This is useful to read chunked + * formats and make sure that one doesn't read beyond the size of the sub-chunk. + * + * Unlike gst_byte_reader_peek_sub_reader(), this function also modifies the + * position of @reader and moves it forward by @size bytes. + * + * Returns: FALSE on error or if @reader does not contain @size more bytes from + * the current position, and otherwise TRUE + * + * Since: 1.6 + */ +gboolean +gst_byte_reader_get_sub_reader (GstByteReader * reader, + GstByteReader * sub_reader, guint size) +{ + return _gst_byte_reader_get_sub_reader_inline (reader, sub_reader, size); +} + +/** + * gst_byte_reader_set_pos: + * @reader: a #GstByteReader instance + * @pos: The new position in bytes + * + * Sets the new position of a #GstByteReader instance to @pos in bytes. + * + * Returns: %TRUE if the position could be set successfully, %FALSE + * otherwise. + */ +gboolean +gst_byte_reader_set_pos (GstByteReader * reader, guint pos) +{ + g_return_val_if_fail (reader != NULL, FALSE); + + if (pos > reader->size) + return FALSE; + + reader->byte = pos; + + return TRUE; +} + +/** + * gst_byte_reader_get_pos: + * @reader: a #GstByteReader instance + * + * Returns the current position of a #GstByteReader instance in bytes. + * + * Returns: The current position of @reader in bytes. + */ +guint +gst_byte_reader_get_pos (const GstByteReader * reader) +{ + return _gst_byte_reader_get_pos_inline (reader); +} + +/** + * gst_byte_reader_get_remaining: + * @reader: a #GstByteReader instance + * + * Returns the remaining number of bytes of a #GstByteReader instance. + * + * Returns: The remaining number of bytes of @reader instance. + */ +guint +gst_byte_reader_get_remaining (const GstByteReader * reader) +{ + return _gst_byte_reader_get_remaining_inline (reader); +} + +/** + * gst_byte_reader_get_size: + * @reader: a #GstByteReader instance + * + * Returns the total number of bytes of a #GstByteReader instance. + * + * Returns: The total number of bytes of @reader instance. + */ +guint +gst_byte_reader_get_size (const GstByteReader * reader) +{ + return _gst_byte_reader_get_size_inline (reader); +} + +#define gst_byte_reader_get_remaining _gst_byte_reader_get_remaining_inline +#define gst_byte_reader_get_size _gst_byte_reader_get_size_inline + +/** + * gst_byte_reader_skip: + * @reader: a #GstByteReader instance + * @nbytes: the number of bytes to skip + * + * Skips @nbytes bytes of the #GstByteReader instance. + * + * Returns: %TRUE if @nbytes bytes could be skipped, %FALSE otherwise. + */ +gboolean +gst_byte_reader_skip (GstByteReader * reader, guint nbytes) +{ + return _gst_byte_reader_skip_inline (reader, nbytes); +} + +/** + * gst_byte_reader_get_uint8: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint8 to store the result + * + * Read an unsigned 8 bit integer into @val and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int8: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint8 to store the result + * + * Read a signed 8 bit integer into @val and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint8: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint8 to store the result + * + * Read an unsigned 8 bit integer into @val but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int8: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint8 to store the result + * + * Read a signed 8 bit integer into @val but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint16_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint16 to store the result + * + * Read an unsigned 16 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int16_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint16 to store the result + * + * Read a signed 16 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint16_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint16 to store the result + * + * Read an unsigned 16 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int16_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint16 to store the result + * + * Read a signed 16 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint16_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint16 to store the result + * + * Read an unsigned 16 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int16_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint16 to store the result + * + * Read a signed 16 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint16_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint16 to store the result + * + * Read an unsigned 16 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int16_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint16 to store the result + * + * Read a signed 16 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint24_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 24 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int24_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 24 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint24_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 24 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int24_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 24 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint24_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 24 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int24_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 24 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint24_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 24 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int24_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 24 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + + +/** + * gst_byte_reader_get_uint32_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 32 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int32_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 32 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint32_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 32 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int32_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 32 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint32_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 32 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int32_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 32 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint32_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint32 to store the result + * + * Read an unsigned 32 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int32_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint32 to store the result + * + * Read a signed 32 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint64_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint64 to store the result + * + * Read an unsigned 64 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int64_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint64 to store the result + * + * Read a signed 64 bit little endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint64_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint64 to store the result + * + * Read an unsigned 64 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int64_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint64 to store the result + * + * Read a signed 64 bit little endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_uint64_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint64 to store the result + * + * Read an unsigned 64 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_int64_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint64 to store the result + * + * Read a signed 64 bit big endian integer into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_uint64_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #guint64 to store the result + * + * Read an unsigned 64 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_int64_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gint64 to store the result + * + * Read a signed 64 bit big endian integer into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +#define GST_BYTE_READER_PEEK_GET(bits,type,name) \ +gboolean \ +gst_byte_reader_get_##name (GstByteReader * reader, type * val) \ +{ \ + return _gst_byte_reader_get_##name##_inline (reader, val); \ +} \ +\ +gboolean \ +gst_byte_reader_peek_##name (const GstByteReader * reader, type * val) \ +{ \ + return _gst_byte_reader_peek_##name##_inline (reader, val); \ +} + +/* *INDENT-OFF* */ + +GST_BYTE_READER_PEEK_GET(8,guint8,uint8) +GST_BYTE_READER_PEEK_GET(8,gint8,int8) + +GST_BYTE_READER_PEEK_GET(16,guint16,uint16_le) +GST_BYTE_READER_PEEK_GET(16,guint16,uint16_be) +GST_BYTE_READER_PEEK_GET(16,gint16,int16_le) +GST_BYTE_READER_PEEK_GET(16,gint16,int16_be) + +GST_BYTE_READER_PEEK_GET(24,guint32,uint24_le) +GST_BYTE_READER_PEEK_GET(24,guint32,uint24_be) +GST_BYTE_READER_PEEK_GET(24,gint32,int24_le) +GST_BYTE_READER_PEEK_GET(24,gint32,int24_be) + +GST_BYTE_READER_PEEK_GET(32,guint32,uint32_le) +GST_BYTE_READER_PEEK_GET(32,guint32,uint32_be) +GST_BYTE_READER_PEEK_GET(32,gint32,int32_le) +GST_BYTE_READER_PEEK_GET(32,gint32,int32_be) + +GST_BYTE_READER_PEEK_GET(64,guint64,uint64_le) +GST_BYTE_READER_PEEK_GET(64,guint64,uint64_be) +GST_BYTE_READER_PEEK_GET(64,gint64,int64_le) +GST_BYTE_READER_PEEK_GET(64,gint64,int64_be) + +/** + * gst_byte_reader_get_float32_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gfloat to store the result + * + * Read a 32 bit little endian floating point value into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_float32_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gfloat to store the result + * + * Read a 32 bit little endian floating point value into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_float32_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gfloat to store the result + * + * Read a 32 bit big endian floating point value into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_float32_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gfloat to store the result + * + * Read a 32 bit big endian floating point value into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_float64_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gdouble to store the result + * + * Read a 64 bit little endian floating point value into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_float64_le: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gdouble to store the result + * + * Read a 64 bit little endian floating point value into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_get_float64_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gdouble to store the result + * + * Read a 64 bit big endian floating point value into @val + * and update the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +/** + * gst_byte_reader_peek_float64_be: + * @reader: a #GstByteReader instance + * @val: (out): Pointer to a #gdouble to store the result + * + * Read a 64 bit big endian floating point value into @val + * but keep the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ + +GST_BYTE_READER_PEEK_GET(32,gfloat,float32_le) +GST_BYTE_READER_PEEK_GET(32,gfloat,float32_be) +GST_BYTE_READER_PEEK_GET(64,gdouble,float64_le) +GST_BYTE_READER_PEEK_GET(64,gdouble,float64_be) + +/* *INDENT-ON* */ + +/** + * gst_byte_reader_get_data: + * @reader: a #GstByteReader instance + * @size: (in): Size in bytes + * @val: (out) (transfer none) (array length=size): address of a + * #guint8 pointer variable in which to store the result + * + * Returns a constant pointer to the current data + * position if at least @size bytes are left and + * updates the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_byte_reader_get_data (GstByteReader * reader, guint size, + const guint8 ** val) +{ + return _gst_byte_reader_get_data_inline (reader, size, val); +} + +/** + * gst_byte_reader_peek_data: + * @reader: a #GstByteReader instance + * @size: (in): Size in bytes + * @val: (out) (transfer none) (array length=size): address of a + * #guint8 pointer variable in which to store the result + * + * Returns a constant pointer to the current data + * position if at least @size bytes are left and + * keeps the current position. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_byte_reader_peek_data (const GstByteReader * reader, guint size, + const guint8 ** val) +{ + return _gst_byte_reader_peek_data_inline (reader, size, val); +} + +/** + * gst_byte_reader_dup_data: + * @reader: a #GstByteReader instance + * @size: (in): Size in bytes + * @val: (out) (transfer full) (array length=size): address of a + * #guint8 pointer variable in which to store the result + * + * Free-function: g_free + * + * Returns a newly-allocated copy of the current data + * position if at least @size bytes are left and + * updates the current position. Free with g_free() when no longer needed. + * + * Returns: %TRUE if successful, %FALSE otherwise. + */ +gboolean +gst_byte_reader_dup_data (GstByteReader * reader, guint size, guint8 ** val) +{ + return _gst_byte_reader_dup_data_inline (reader, size, val); +} + +/* Special optimized scan for mask 0xffffff00 and pattern 0x00000100 */ +static inline gint +_scan_for_start_code (const guint8 * data, guint size) +{ + guint8 *pdata = (guint8 *) data; + guint8 *pend = (guint8 *) (data + size - 4); + + while (pdata <= pend) { + if (pdata[2] > 1) { + pdata += 3; + } else if (pdata[1]) { + pdata += 2; + } else if (pdata[0] || pdata[2] != 1) { + pdata++; + } else { + return (pdata - data); + } + } + + /* nothing found */ + return -1; +} + +static inline guint +_masked_scan_uint32_peek (const GstByteReader * reader, + guint32 mask, guint32 pattern, guint offset, guint size, guint32 * value) +{ + const guint8 *data; + guint32 state; + guint i; + + g_return_val_if_fail (size > 0, -1); + g_return_val_if_fail ((guint64) offset + size <= reader->size - reader->byte, + -1); + + /* we can't find the pattern with less than 4 bytes */ + if (G_UNLIKELY (size < 4)) + return -1; + + data = reader->data + reader->byte + offset; + + /* Handle special case found in MPEG and H264 */ + if ((pattern == 0x00000100) && (mask == 0xffffff00)) { + gint ret = _scan_for_start_code (data, size); + + if (ret == -1) + return ret; + + if (value != NULL) + *value = (1 << 8) | data[ret + 3]; + + return ret + offset; + } + + /* set the state to something that does not match */ + state = ~pattern; + + /* now find data */ + for (i = 0; i < size; i++) { + /* throw away one byte and move in the next byte */ + state = ((state << 8) | data[i]); + if (G_UNLIKELY ((state & mask) == pattern)) { + /* we have a match but we need to have skipped at + * least 4 bytes to fill the state. */ + if (G_LIKELY (i >= 3)) { + if (value) + *value = state; + return offset + i - 3; + } + } + } + + /* nothing found */ + return -1; +} + + +/** + * gst_byte_reader_masked_scan_uint32: + * @reader: a #GstByteReader + * @mask: mask to apply to data before matching against @pattern + * @pattern: pattern to match (after mask is applied) + * @offset: offset from which to start scanning, relative to the current + * position + * @size: number of bytes to scan from offset + * + * Scan for pattern @pattern with applied mask @mask in the byte reader data, + * starting from offset @offset relative to the current position. + * + * The bytes in @pattern and @mask are interpreted left-to-right, regardless + * of endianness. All four bytes of the pattern must be present in the + * byte reader data for it to match, even if the first or last bytes are masked + * out. + * + * It is an error to call this function without making sure that there is + * enough data (offset+size bytes) in the byte reader. + * + * Returns: offset of the first match, or -1 if no match was found. + * + * Example: + * |[ + * // Assume the reader contains 0x00 0x01 0x02 ... 0xfe 0xff + * + * gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x00010203, 0, 256); + * // -> returns 0 + * gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x00010203, 1, 255); + * // -> returns -1 + * gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x01020304, 1, 255); + * // -> returns 1 + * gst_byte_reader_masked_scan_uint32 (reader, 0xffff, 0x0001, 0, 256); + * // -> returns -1 + * gst_byte_reader_masked_scan_uint32 (reader, 0xffff, 0x0203, 0, 256); + * // -> returns 0 + * gst_byte_reader_masked_scan_uint32 (reader, 0xffff0000, 0x02030000, 0, 256); + * // -> returns 2 + * gst_byte_reader_masked_scan_uint32 (reader, 0xffff0000, 0x02030000, 0, 4); + * // -> returns -1 + * ]| + */ +guint +gst_byte_reader_masked_scan_uint32 (const GstByteReader * reader, guint32 mask, + guint32 pattern, guint offset, guint size) +{ + return _masked_scan_uint32_peek (reader, mask, pattern, offset, size, NULL); +} + +/** + * gst_byte_reader_masked_scan_uint32_peek: + * @reader: a #GstByteReader + * @mask: mask to apply to data before matching against @pattern + * @pattern: pattern to match (after mask is applied) + * @offset: offset from which to start scanning, relative to the current + * position + * @size: number of bytes to scan from offset + * @value: (out): pointer to uint32 to return matching data + * + * Scan for pattern @pattern with applied mask @mask in the byte reader data, + * starting from offset @offset relative to the current position. + * + * The bytes in @pattern and @mask are interpreted left-to-right, regardless + * of endianness. All four bytes of the pattern must be present in the + * byte reader data for it to match, even if the first or last bytes are masked + * out. + * + * It is an error to call this function without making sure that there is + * enough data (offset+size bytes) in the byte reader. + * + * Returns: offset of the first match, or -1 if no match was found. + * + * Since: 1.6 + */ +guint +gst_byte_reader_masked_scan_uint32_peek (const GstByteReader * reader, + guint32 mask, guint32 pattern, guint offset, guint size, guint32 * value) +{ + return _masked_scan_uint32_peek (reader, mask, pattern, offset, size, value); +} + +#define GST_BYTE_READER_SCAN_STRING(bits) \ +static guint \ +gst_byte_reader_scan_string_utf##bits (const GstByteReader * reader) \ +{ \ + guint len, off, max_len; \ + \ + max_len = (reader->size - reader->byte) / sizeof (guint##bits); \ + \ + /* need at least a single NUL terminator */ \ + if (max_len < 1) \ + return 0; \ + \ + len = 0; \ + off = reader->byte; \ + /* endianness does not matter if we are looking for a NUL terminator */ \ + while (GST_READ_UINT##bits##_LE (&reader->data[off]) != 0) { \ + ++len; \ + off += sizeof (guint##bits); \ + /* have we reached the end without finding a NUL terminator? */ \ + if (len == max_len) \ + return 0; \ + } \ + /* return size in bytes including the NUL terminator (hence the +1) */ \ + return (len + 1) * sizeof (guint##bits); \ +} + +#define GST_READ_UINT8_LE GST_READ_UINT8 +GST_BYTE_READER_SCAN_STRING (8); +#undef GST_READ_UINT8_LE +GST_BYTE_READER_SCAN_STRING (16); +GST_BYTE_READER_SCAN_STRING (32); + +#define GST_BYTE_READER_SKIP_STRING(bits) \ +gboolean \ +gst_byte_reader_skip_string_utf##bits (GstByteReader * reader) \ +{ \ + guint size; /* size in bytes including the terminator */ \ + \ + g_return_val_if_fail (reader != NULL, FALSE); \ + \ + size = gst_byte_reader_scan_string_utf##bits (reader); \ + reader->byte += size; \ + return (size > 0); \ +} + +/** + * gst_byte_reader_skip_string: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated string in the #GstByteReader instance, advancing + * the current position to the byte after the string. This will work for + * any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + */ +/** + * gst_byte_reader_skip_string_utf8: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated string in the #GstByteReader instance, advancing + * the current position to the byte after the string. This will work for + * any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + */ +GST_BYTE_READER_SKIP_STRING (8); + +/** + * gst_byte_reader_skip_string_utf16: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated UTF-16 string in the #GstByteReader instance, + * advancing the current position to the byte after the string. + * + * No input checking for valid UTF-16 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + */ +GST_BYTE_READER_SKIP_STRING (16); + +/** + * gst_byte_reader_skip_string_utf32: + * @reader: a #GstByteReader instance + * + * Skips a NUL-terminated UTF-32 string in the #GstByteReader instance, + * advancing the current position to the byte after the string. + * + * No input checking for valid UTF-32 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + */ +GST_BYTE_READER_SKIP_STRING (32); + +/** + * gst_byte_reader_peek_string: + * @reader: a #GstByteReader instance + * @str: (out) (transfer none) (array zero-terminated=1): address of a + * #gchar pointer variable in which to store the result + * + * Returns a constant pointer to the current data position if there is + * a NUL-terminated string in the data (this could be just a NUL terminator). + * The current position will be maintained. This will work for any + * NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + */ +/** + * gst_byte_reader_peek_string_utf8: + * @reader: a #GstByteReader instance + * @str: (out) (transfer none) (array zero-terminated=1): address of a + * #gchar pointer variable in which to store the result + * + * Returns a constant pointer to the current data position if there is + * a NUL-terminated string in the data (this could be just a NUL terminator). + * The current position will be maintained. This will work for any + * NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be skipped, %FALSE otherwise. + */ +gboolean +gst_byte_reader_peek_string_utf8 (const GstByteReader * reader, + const gchar ** str) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + if (gst_byte_reader_scan_string_utf8 (reader) > 0) { + *str = (const gchar *) (reader->data + reader->byte); + } else { + *str = NULL; + } + return (*str != NULL); +} + +/** + * gst_byte_reader_get_string_utf8: + * @reader: a #GstByteReader instance + * @str: (out) (transfer none) (array zero-terminated=1): address of a + * #gchar pointer variable in which to store the result + * + * Returns a constant pointer to the current data position if there is + * a NUL-terminated string in the data (this could be just a NUL terminator), + * advancing the current position to the byte after the string. This will work + * for any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. + * + * No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be found, %FALSE otherwise. + */ +gboolean +gst_byte_reader_get_string_utf8 (GstByteReader * reader, const gchar ** str) +{ + guint size; /* size in bytes including the terminator */ + + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + size = gst_byte_reader_scan_string_utf8 (reader); + if (size == 0) { + *str = NULL; + return FALSE; + } + + *str = (const gchar *) (reader->data + reader->byte); + reader->byte += size; + return TRUE; +} + +#define GST_BYTE_READER_DUP_STRING(bits,type) \ +gboolean \ +gst_byte_reader_dup_string_utf##bits (GstByteReader * reader, type ** str) \ +{ \ + guint size; /* size in bytes including the terminator */ \ + \ + g_return_val_if_fail (reader != NULL, FALSE); \ + g_return_val_if_fail (str != NULL, FALSE); \ + \ + size = gst_byte_reader_scan_string_utf##bits (reader); \ + if (size == 0) { \ + *str = NULL; \ + return FALSE; \ + } \ + *str = g_memdup2 (reader->data + reader->byte, size); \ + reader->byte += size; \ + return TRUE; \ +} + +/** + * gst_byte_reader_dup_string_utf8: + * @reader: a #GstByteReader instance + * @str: (out) (transfer full) (array zero-terminated=1): address of a + * #gchar pointer variable in which to store the result + * + * Free-function: g_free + * + * FIXME:Reads (copies) a NUL-terminated string in the #GstByteReader instance, + * advancing the current position to the byte after the string. This will work + * for any NUL-terminated string with a character width of 8 bits, so ASCII, + * UTF-8, ISO-8859-N etc. No input checking for valid UTF-8 is done. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Returns: %TRUE if a string could be read into @str, %FALSE otherwise. The + * string put into @str must be freed with g_free() when no longer needed. + */ +GST_BYTE_READER_DUP_STRING (8, gchar); + +/** + * gst_byte_reader_dup_string_utf16: + * @reader: a #GstByteReader instance + * @str: (out) (transfer full) (array zero-terminated=1): address of a + * #guint16 pointer variable in which to store the result + * + * Free-function: g_free + * + * Returns a newly-allocated copy of the current data position if there is + * a NUL-terminated UTF-16 string in the data (this could be an empty string + * as well), and advances the current position. + * + * No input checking for valid UTF-16 is done. This function is endianness + * agnostic - you should not assume the UTF-16 characters are in host + * endianness. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Note: there is no peek or get variant of this function to ensure correct + * byte alignment of the UTF-16 string. + * + * Returns: %TRUE if a string could be read, %FALSE otherwise. The + * string put into @str must be freed with g_free() when no longer needed. + */ +GST_BYTE_READER_DUP_STRING (16, guint16); + +/** + * gst_byte_reader_dup_string_utf32: + * @reader: a #GstByteReader instance + * @str: (out) (transfer full) (array zero-terminated=1): address of a + * #guint32 pointer variable in which to store the result + * + * Free-function: g_free + * + * Returns a newly-allocated copy of the current data position if there is + * a NUL-terminated UTF-32 string in the data (this could be an empty string + * as well), and advances the current position. + * + * No input checking for valid UTF-32 is done. This function is endianness + * agnostic - you should not assume the UTF-32 characters are in host + * endianness. + * + * This function will fail if no NUL-terminator was found in in the data. + * + * Note: there is no peek or get variant of this function to ensure correct + * byte alignment of the UTF-32 string. + * + * Returns: %TRUE if a string could be read, %FALSE otherwise. The + * string put into @str must be freed with g_free() when no longer needed. + */ +GST_BYTE_READER_DUP_STRING (32, guint32); diff --git a/src/h265_parser/gstbytereader.h b/src/h265_parser/gstbytereader.h new file mode 100644 index 0000000..2130a8a --- /dev/null +++ b/src/h265_parser/gstbytereader.h @@ -0,0 +1,684 @@ +/* GStreamer byte reader + * + * Copyright (C) 2008 Sebastian Dröge . + * Copyright (C) 2009 Tim-Philipp Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_BYTE_READER_H__ +#define __GST_BYTE_READER_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_BYTE_READER(reader) ((GstByteReader *) (reader)) + +/** + * GstByteReader: + * @data: (array length=size): Data from which the bit reader will + * read + * @size: Size of @data in bytes + * @byte: Current byte position + * + * A byte reader instance. + */ +typedef struct { + const guint8 *data; + guint size; + + guint byte; /* Byte position */ + + /* < private > */ + gpointer _gst_reserved[GST_PADDING]; +} GstByteReader; + +GST_BASE_API +GstByteReader * gst_byte_reader_new (const guint8 *data, guint size) G_GNUC_MALLOC; + +GST_BASE_API +void gst_byte_reader_free (GstByteReader *reader); + +GST_BASE_API +void gst_byte_reader_init (GstByteReader *reader, const guint8 *data, guint size); + +GST_BASE_API +gboolean gst_byte_reader_peek_sub_reader (GstByteReader * reader, + GstByteReader * sub_reader, + guint size); +GST_BASE_API +gboolean gst_byte_reader_get_sub_reader (GstByteReader * reader, + GstByteReader * sub_reader, + guint size); +GST_BASE_API +gboolean gst_byte_reader_set_pos (GstByteReader *reader, guint pos); + +GST_BASE_API +guint gst_byte_reader_get_pos (const GstByteReader *reader); + +GST_BASE_API +guint gst_byte_reader_get_remaining (const GstByteReader *reader); + +GST_BASE_API +guint gst_byte_reader_get_size (const GstByteReader *reader); + +GST_BASE_API +gboolean gst_byte_reader_skip (GstByteReader *reader, guint nbytes); + +GST_BASE_API +gboolean gst_byte_reader_get_uint8 (GstByteReader *reader, guint8 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int8 (GstByteReader *reader, gint8 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint16_le (GstByteReader *reader, guint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int16_le (GstByteReader *reader, gint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint16_be (GstByteReader *reader, guint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int16_be (GstByteReader *reader, gint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint24_le (GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int24_le (GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint24_be (GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int24_be (GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint32_le (GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int32_le (GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint32_be (GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int32_be (GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint64_le (GstByteReader *reader, guint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int64_le (GstByteReader *reader, gint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_uint64_be (GstByteReader *reader, guint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_int64_be (GstByteReader *reader, gint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint8 (const GstByteReader *reader, guint8 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int8 (const GstByteReader *reader, gint8 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint16_le (const GstByteReader *reader, guint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int16_le (const GstByteReader *reader, gint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint16_be (const GstByteReader *reader, guint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int16_be (const GstByteReader *reader, gint16 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint24_le (const GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int24_le (const GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint24_be (const GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int24_be (const GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint32_le (const GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int32_le (const GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint32_be (const GstByteReader *reader, guint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int32_be (const GstByteReader *reader, gint32 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint64_le (const GstByteReader *reader, guint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int64_le (const GstByteReader *reader, gint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_uint64_be (const GstByteReader *reader, guint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_int64_be (const GstByteReader *reader, gint64 *val); + +GST_BASE_API +gboolean gst_byte_reader_get_float32_le (GstByteReader *reader, gfloat *val); + +GST_BASE_API +gboolean gst_byte_reader_get_float32_be (GstByteReader *reader, gfloat *val); + +GST_BASE_API +gboolean gst_byte_reader_get_float64_le (GstByteReader *reader, gdouble *val); + +GST_BASE_API +gboolean gst_byte_reader_get_float64_be (GstByteReader *reader, gdouble *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_float32_le (const GstByteReader *reader, gfloat *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_float32_be (const GstByteReader *reader, gfloat *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_float64_le (const GstByteReader *reader, gdouble *val); + +GST_BASE_API +gboolean gst_byte_reader_peek_float64_be (const GstByteReader *reader, gdouble *val); + +GST_BASE_API +gboolean gst_byte_reader_dup_data (GstByteReader * reader, guint size, guint8 ** val); + +GST_BASE_API +gboolean gst_byte_reader_get_data (GstByteReader * reader, guint size, const guint8 ** val); + +GST_BASE_API +gboolean gst_byte_reader_peek_data (const GstByteReader * reader, guint size, const guint8 ** val); + +#define gst_byte_reader_dup_string(reader,str) \ + gst_byte_reader_dup_string_utf8(reader,str) + +GST_BASE_API +gboolean gst_byte_reader_dup_string_utf8 (GstByteReader * reader, gchar ** str); + +GST_BASE_API +gboolean gst_byte_reader_dup_string_utf16 (GstByteReader * reader, guint16 ** str); + +GST_BASE_API +gboolean gst_byte_reader_dup_string_utf32 (GstByteReader * reader, guint32 ** str); + +#define gst_byte_reader_skip_string(reader) \ + gst_byte_reader_skip_string_utf8(reader) + +GST_BASE_API +gboolean gst_byte_reader_skip_string_utf8 (GstByteReader * reader); + +GST_BASE_API +gboolean gst_byte_reader_skip_string_utf16 (GstByteReader * reader); + +GST_BASE_API +gboolean gst_byte_reader_skip_string_utf32 (GstByteReader * reader); + +#define gst_byte_reader_get_string(reader,str) \ + gst_byte_reader_get_string_utf8(reader,str) + +#define gst_byte_reader_peek_string(reader,str) \ + gst_byte_reader_peek_string_utf8(reader,str) + +GST_BASE_API +gboolean gst_byte_reader_get_string_utf8 (GstByteReader * reader, const gchar ** str); + +GST_BASE_API +gboolean gst_byte_reader_peek_string_utf8 (const GstByteReader * reader, const gchar ** str); + +GST_BASE_API +guint gst_byte_reader_masked_scan_uint32 (const GstByteReader * reader, + guint32 mask, + guint32 pattern, + guint offset, + guint size); +GST_BASE_API +guint gst_byte_reader_masked_scan_uint32_peek (const GstByteReader * reader, + guint32 mask, + guint32 pattern, + guint offset, + guint size, + guint32 * value); + +/** + * GST_BYTE_READER_INIT: + * @data: Data from which the #GstByteReader should read + * @size: Size of @data in bytes + * + * A #GstByteReader must be initialized with this macro, before it can be + * used. This macro can used be to initialize a variable, but it cannot + * be assigned to a variable. In that case you have to use + * gst_byte_reader_init(). + */ +#define GST_BYTE_READER_INIT(data, size) {data, size, 0} + +/* unchecked variants */ +static inline void +gst_byte_reader_skip_unchecked (GstByteReader * reader, guint nbytes) +{ + reader->byte += nbytes; +} + +#define __GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(bits,type,lower,upper,adj) \ +\ +static inline type \ +gst_byte_reader_peek_##lower##_unchecked (const GstByteReader * reader) \ +{ \ + type val = (type) GST_READ_##upper (reader->data + reader->byte); \ + adj \ + return val; \ +} \ +\ +static inline type \ +gst_byte_reader_get_##lower##_unchecked (GstByteReader * reader) \ +{ \ + type val = gst_byte_reader_peek_##lower##_unchecked (reader); \ + reader->byte += bits / 8; \ + return val; \ +} + +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(8,guint8,uint8,UINT8,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(8,gint8,int8,UINT8,/* */) + +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,guint16,uint16_le,UINT16_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,guint16,uint16_be,UINT16_BE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,gint16,int16_le,UINT16_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,gint16,int16_be,UINT16_BE,/* */) + +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,guint32,uint32_le,UINT32_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,guint32,uint32_be,UINT32_BE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gint32,int32_le,UINT32_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gint32,int32_be,UINT32_BE,/* */) + +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,guint32,uint24_le,UINT24_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,guint32,uint24_be,UINT24_BE,/* */) + +/* fix up the sign for 24-bit signed ints stored in 32-bit signed ints */ +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,gint32,int24_le,UINT24_LE, + if (val & 0x00800000) val |= 0xff000000;) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,gint32,int24_be,UINT24_BE, + if (val & 0x00800000) val |= 0xff000000;) + +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,guint64,uint64_le,UINT64_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,guint64,uint64_be,UINT64_BE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gint64,int64_le,UINT64_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gint64,int64_be,UINT64_BE,/* */) + +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gfloat,float32_le,FLOAT_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gfloat,float32_be,FLOAT_BE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gdouble,float64_le,DOUBLE_LE,/* */) +__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gdouble,float64_be,DOUBLE_BE,/* */) + +#undef __GET_PEEK_BITS_UNCHECKED + +static inline const guint8 * +gst_byte_reader_peek_data_unchecked (const GstByteReader * reader) +{ + return (const guint8 *) (reader->data + reader->byte); +} + +static inline const guint8 * +gst_byte_reader_get_data_unchecked (GstByteReader * reader, guint size) +{ + const guint8 *data; + + data = gst_byte_reader_peek_data_unchecked (reader); + gst_byte_reader_skip_unchecked (reader, size); + return data; +} + +static inline guint8 * +gst_byte_reader_dup_data_unchecked (GstByteReader * reader, guint size) +{ + gconstpointer data = gst_byte_reader_get_data_unchecked (reader, size); + guint8 *dup_data = (guint8 *) g_malloc (size); + + memcpy (dup_data, data, size); + return dup_data; +} + +/* Unchecked variants that should not be used */ +static inline guint +_gst_byte_reader_get_pos_unchecked (const GstByteReader * reader) +{ + return reader->byte; +} + +static inline guint +_gst_byte_reader_get_remaining_unchecked (const GstByteReader * reader) +{ + return reader->size - reader->byte; +} + +static inline guint +_gst_byte_reader_get_size_unchecked (const GstByteReader * reader) +{ + return reader->size; +} + +/* inlined variants (do not use directly) */ + +static inline guint +_gst_byte_reader_get_remaining_inline (const GstByteReader * reader) +{ + g_return_val_if_fail (reader != NULL, 0); + + return _gst_byte_reader_get_remaining_unchecked (reader); +} + +static inline guint +_gst_byte_reader_get_size_inline (const GstByteReader * reader) +{ + g_return_val_if_fail (reader != NULL, 0); + + return _gst_byte_reader_get_size_unchecked (reader); +} + +#define __GST_BYTE_READER_GET_PEEK_BITS_INLINE(bits,type,name) \ +\ +static inline gboolean \ +_gst_byte_reader_peek_##name##_inline (const GstByteReader * reader, type * val) \ +{ \ + g_return_val_if_fail (reader != NULL, FALSE); \ + g_return_val_if_fail (val != NULL, FALSE); \ + \ + if (_gst_byte_reader_get_remaining_unchecked (reader) < (bits / 8)) \ + return FALSE; \ +\ + *val = gst_byte_reader_peek_##name##_unchecked (reader); \ + return TRUE; \ +} \ +\ +static inline gboolean \ +_gst_byte_reader_get_##name##_inline (GstByteReader * reader, type * val) \ +{ \ + g_return_val_if_fail (reader != NULL, FALSE); \ + g_return_val_if_fail (val != NULL, FALSE); \ + \ + if (_gst_byte_reader_get_remaining_unchecked (reader) < (bits / 8)) \ + return FALSE; \ +\ + *val = gst_byte_reader_get_##name##_unchecked (reader); \ + return TRUE; \ +} + +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(8,guint8,uint8) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(8,gint8,int8) + +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,guint16,uint16_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,guint16,uint16_be) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,gint16,int16_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,gint16,int16_be) + +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,guint32,uint32_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,guint32,uint32_be) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gint32,int32_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gint32,int32_be) + +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,guint32,uint24_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,guint32,uint24_be) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,gint32,int24_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,gint32,int24_be) + +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,guint64,uint64_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,guint64,uint64_be) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gint64,int64_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gint64,int64_be) + +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gfloat,float32_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gfloat,float32_be) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gdouble,float64_le) +__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gdouble,float64_be) + +#undef __GST_BYTE_READER_GET_PEEK_BITS_INLINE + +#ifndef GST_BYTE_READER_DISABLE_INLINES + +#define gst_byte_reader_init(reader,data,size) \ + _gst_byte_reader_init_inline(reader,data,size) + +#define gst_byte_reader_get_remaining(reader) \ + _gst_byte_reader_get_remaining_inline(reader) + +#define gst_byte_reader_get_size(reader) \ + _gst_byte_reader_get_size_inline(reader) + +#define gst_byte_reader_get_pos(reader) \ + _gst_byte_reader_get_pos_inline(reader) + +/* we use defines here so we can add the G_LIKELY() */ +#define gst_byte_reader_get_uint8(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint8_inline(reader,val)) +#define gst_byte_reader_get_int8(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int8_inline(reader,val)) +#define gst_byte_reader_get_uint16_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint16_le_inline(reader,val)) +#define gst_byte_reader_get_int16_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int16_le_inline(reader,val)) +#define gst_byte_reader_get_uint16_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint16_be_inline(reader,val)) +#define gst_byte_reader_get_int16_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int16_be_inline(reader,val)) +#define gst_byte_reader_get_uint24_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint24_le_inline(reader,val)) +#define gst_byte_reader_get_int24_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int24_le_inline(reader,val)) +#define gst_byte_reader_get_uint24_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint24_be_inline(reader,val)) +#define gst_byte_reader_get_int24_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int24_be_inline(reader,val)) +#define gst_byte_reader_get_uint32_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint32_le_inline(reader,val)) +#define gst_byte_reader_get_int32_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int32_le_inline(reader,val)) +#define gst_byte_reader_get_uint32_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint32_be_inline(reader,val)) +#define gst_byte_reader_get_int32_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int32_be_inline(reader,val)) +#define gst_byte_reader_get_uint64_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint64_le_inline(reader,val)) +#define gst_byte_reader_get_int64_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int64_le_inline(reader,val)) +#define gst_byte_reader_get_uint64_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_uint64_be_inline(reader,val)) +#define gst_byte_reader_get_int64_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_int64_be_inline(reader,val)) + +#define gst_byte_reader_peek_uint8(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint8_inline(reader,val)) +#define gst_byte_reader_peek_int8(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int8_inline(reader,val)) +#define gst_byte_reader_peek_uint16_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint16_le_inline(reader,val)) +#define gst_byte_reader_peek_int16_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int16_le_inline(reader,val)) +#define gst_byte_reader_peek_uint16_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint16_be_inline(reader,val)) +#define gst_byte_reader_peek_int16_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int16_be_inline(reader,val)) +#define gst_byte_reader_peek_uint24_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint24_le_inline(reader,val)) +#define gst_byte_reader_peek_int24_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int24_le_inline(reader,val)) +#define gst_byte_reader_peek_uint24_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint24_be_inline(reader,val)) +#define gst_byte_reader_peek_int24_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int24_be_inline(reader,val)) +#define gst_byte_reader_peek_uint32_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint32_le_inline(reader,val)) +#define gst_byte_reader_peek_int32_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int32_le_inline(reader,val)) +#define gst_byte_reader_peek_uint32_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint32_be_inline(reader,val)) +#define gst_byte_reader_peek_int32_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int32_be_inline(reader,val)) +#define gst_byte_reader_peek_uint64_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint64_le_inline(reader,val)) +#define gst_byte_reader_peek_int64_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int64_le_inline(reader,val)) +#define gst_byte_reader_peek_uint64_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_uint64_be_inline(reader,val)) +#define gst_byte_reader_peek_int64_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_int64_be_inline(reader,val)) + +#define gst_byte_reader_get_float32_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_float32_le_inline(reader,val)) +#define gst_byte_reader_get_float32_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_float32_be_inline(reader,val)) +#define gst_byte_reader_get_float64_le(reader,val) \ + G_LIKELY(_gst_byte_reader_get_float64_le_inline(reader,val)) +#define gst_byte_reader_get_float64_be(reader,val) \ + G_LIKELY(_gst_byte_reader_get_float64_be_inline(reader,val)) +#define gst_byte_reader_peek_float32_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_float32_le_inline(reader,val)) +#define gst_byte_reader_peek_float32_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_float32_be_inline(reader,val)) +#define gst_byte_reader_peek_float64_le(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_float64_le_inline(reader,val)) +#define gst_byte_reader_peek_float64_be(reader,val) \ + G_LIKELY(_gst_byte_reader_peek_float64_be_inline(reader,val)) + +#endif /* GST_BYTE_READER_DISABLE_INLINES */ + +static inline void +_gst_byte_reader_init_inline (GstByteReader * reader, const guint8 * data, guint size) +{ + g_return_if_fail (reader != NULL); + + reader->data = data; + reader->size = size; + reader->byte = 0; +} + +static inline gboolean +_gst_byte_reader_peek_sub_reader_inline (GstByteReader * reader, + GstByteReader * sub_reader, guint size) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (sub_reader != NULL, FALSE); + + if (_gst_byte_reader_get_remaining_unchecked (reader) < size) + return FALSE; + + sub_reader->data = reader->data + reader->byte; + sub_reader->byte = 0; + sub_reader->size = size; + return TRUE; +} + +static inline gboolean +_gst_byte_reader_get_sub_reader_inline (GstByteReader * reader, + GstByteReader * sub_reader, guint size) +{ + if (!_gst_byte_reader_peek_sub_reader_inline (reader, sub_reader, size)) + return FALSE; + gst_byte_reader_skip_unchecked (reader, size); + return TRUE; +} + +static inline gboolean +_gst_byte_reader_dup_data_inline (GstByteReader * reader, guint size, guint8 ** val) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (val != NULL, FALSE); + + if (G_UNLIKELY (size > reader->size || _gst_byte_reader_get_remaining_unchecked (reader) < size)) + return FALSE; + + *val = gst_byte_reader_dup_data_unchecked (reader, size); + return TRUE; +} + +static inline gboolean +_gst_byte_reader_get_data_inline (GstByteReader * reader, guint size, const guint8 ** val) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (val != NULL, FALSE); + + if (G_UNLIKELY (size > reader->size || _gst_byte_reader_get_remaining_unchecked (reader) < size)) + return FALSE; + + *val = gst_byte_reader_get_data_unchecked (reader, size); + return TRUE; +} + +static inline gboolean +_gst_byte_reader_peek_data_inline (const GstByteReader * reader, guint size, const guint8 ** val) +{ + g_return_val_if_fail (reader != NULL, FALSE); + g_return_val_if_fail (val != NULL, FALSE); + + if (G_UNLIKELY (size > reader->size || _gst_byte_reader_get_remaining_unchecked (reader) < size)) + return FALSE; + + *val = gst_byte_reader_peek_data_unchecked (reader); + return TRUE; +} + +static inline guint +_gst_byte_reader_get_pos_inline (const GstByteReader * reader) +{ + g_return_val_if_fail (reader != NULL, 0); + + return _gst_byte_reader_get_pos_unchecked (reader); +} + +static inline gboolean +_gst_byte_reader_skip_inline (GstByteReader * reader, guint nbytes) +{ + g_return_val_if_fail (reader != NULL, FALSE); + + if (G_UNLIKELY (_gst_byte_reader_get_remaining_unchecked (reader) < nbytes)) + return FALSE; + + reader->byte += nbytes; + return TRUE; +} + +#ifndef GST_BYTE_READER_DISABLE_INLINES + +#define gst_byte_reader_dup_data(reader,size,val) \ + G_LIKELY(_gst_byte_reader_dup_data_inline(reader,size,val)) +#define gst_byte_reader_get_data(reader,size,val) \ + G_LIKELY(_gst_byte_reader_get_data_inline(reader,size,val)) +#define gst_byte_reader_peek_data(reader,size,val) \ + G_LIKELY(_gst_byte_reader_peek_data_inline(reader,size,val)) +#define gst_byte_reader_skip(reader,nbytes) \ + G_LIKELY(_gst_byte_reader_skip_inline(reader,nbytes)) + +#endif /* GST_BYTE_READER_DISABLE_INLINES */ + +G_END_DECLS + +#endif /* __GST_BYTE_READER_H__ */ diff --git a/src/h265_parser/gsth265parser.c b/src/h265_parser/gsth265parser.c new file mode 100644 index 0000000..1b87956 --- /dev/null +++ b/src/h265_parser/gsth265parser.c @@ -0,0 +1,5293 @@ +/* Gstreamer H.265 bitstream parser + * Copyright (C) 2012 Intel Corporation + * Copyright (C) 2013 Sreerenj Balachandran + * + * Contact: Sreerenj Balachandran + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:gsth265parser + * @title: GstH265Parser + * @short_description: Convenience library for h265 video bitstream parsing. + * + * It offers you bitstream parsing in HEVC mode and non-HEVC mode. To identify + * Nals in a bitstream and parse its headers, you should call: + * + * * gst_h265_parser_identify_nalu() to identify the following nalu in + * non-HEVC bitstreams + * + * * gst_h265_parser_identify_nalu_hevc() to identify the nalu in + * HEVC bitstreams + * + * Then, depending on the #GstH265NalUnitType of the newly parsed #GstH265NalUnit, + * you should call the differents functions to parse the structure: + * + * * From %GST_H265_NAL_SLICE_TRAIL_N to %GST_H265_NAL_SLICE_CRA_NUT: gst_h265_parser_parse_slice_hdr() + * + * * `GST_H265_NAL_*_SEI`: gst_h265_parser_parse_sei() + * + * * %GST_H265_NAL_VPS: gst_h265_parser_parse_vps() + * + * * %GST_H265_NAL_SPS: gst_h265_parser_parse_sps() + * + * * %GST_H265_NAL_PPS: #gst_h265_parser_parse_pps() + * + * * Any other: gst_h265_parser_parse_nal() + * + * Note: You should always call gst_h265_parser_parse_nal() if you don't + * actually need #GstH265NalUnitType to be parsed for your personal use, in + * order to guarantee that the #GstH265Parser is always up to date. + * + * For more details about the structures, look at the ITU-T H.265 + * specifications, you can download them from: + * + * * ITU-T H.265: http://www.itu.int/rec/T-REC-H.265 + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "nalutils.h" +#include "gsth265parser.h" + +#include +#include + +#define MAX_DPB_SIZE 16 + +/* ITU-T H.265 (V10) (07/2024) A.4.2, Table A.8: MaxSliceSegmentsPerPicture */ +#define GST_H265_MAX_SLICE_SEGMENTS_PER_PICTURE 1800 + +#ifndef GST_DISABLE_GST_DEBUG +#define GST_CAT_DEFAULT gst_h265_debug_category_get() +static GstDebugCategory * +gst_h265_debug_category_get (void) +{ + static gsize cat_gonce = 0; + + if (g_once_init_enter (&cat_gonce)) { + GstDebugCategory *cat = NULL; + + GST_DEBUG_CATEGORY_INIT (cat, "codecparsers_h265", 0, "h265 parse library"); + + g_once_init_leave (&cat_gonce, (gsize) cat); + } + + return (GstDebugCategory *) cat_gonce; +} +#endif /* GST_DISABLE_GST_DEBUG */ + +/**** Default scaling_lists according to Table 7-5 and 7-6 *****/ + +/* Table 7-5 */ +static const guint8 default_scaling_list0[16] = { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16 +}; + +/* Combined the values in Table 7-6 to make the calculation easier + * Default scaling list of 8x8 and 16x16 matrices for matrixId = 0, 1 and 2 + * Default scaling list of 32x32 matrix for matrixId = 0 + */ +static const guint8 default_scaling_list1[64] = { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, + 17, 16, 17, 18, 17, 18, 18, 17, 18, 21, 19, 20, + 21, 20, 19, 21, 24, 22, 22, 24, 24, 22, 22, 24, + 25, 25, 27, 30, 27, 25, 25, 29, 31, 35, 35, 31, + 29, 36, 41, 44, 41, 36, 47, 54, 54, 47, 65, 70, + 65, 88, 88, 115 +}; + +/* Combined the values in Table 7-6 to make the calculation easier + * Default scaling list of 8x8 and 16x16 matrices for matrixId = 3, 4 and 5 + * Default scaling list of 32x32 matrix for matrixId = 1 + */ +static const guint8 default_scaling_list2[64] = { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, + 17, 17, 17, 18, 18, 18, 18, 18, 18, 20, 20, 20, + 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 28, 28, 28, 28, 28, + 28, 33, 33, 33, 33, 33, 41, 41, 41, 41, 54, 54, + 54, 71, 71, 91 +}; + +static const guint8 zigzag_4x4[16] = { + 0, 1, 4, 8, + 5, 2, 3, 6, + 9, 12, 13, 10, + 7, 11, 14, 15, +}; + +static const guint8 zigzag_8x8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static const guint8 uprightdiagonal_4x4[16] = { + 0, 4, 1, 8, + 5, 2, 12, 9, + 6, 3, 13, 10, + 7, 14, 11, 15 +}; + +static const guint8 uprightdiagonal_8x8[64] = { + 0, 8, 1, 16, 9, 2, 24, 17, + 10, 3, 32, 25, 18, 11, 4, 40, + 33, 26, 19, 12, 5, 48, 41, 34, + 27, 20, 13, 6, 56, 49, 42, 35, + 28, 21, 14, 7, 57, 50, 43, 36, + 29, 22, 15, 58, 51, 44, 37, 30, + 23, 59, 52, 45, 38, 31, 60, 53, + 46, 39, 61, 54, 47, 62, 55, 63 +}; + +typedef struct +{ + guint par_n, par_d; +} PAR; + +/* Table E-1 - Meaning of sample aspect ratio indicator (1..16) */ +static const PAR aspect_ratios[17] = { + {0, 0}, + {1, 1}, + {12, 11}, + {10, 11}, + {16, 11}, + {40, 33}, + {24, 11}, + {20, 11}, + {32, 11}, + {80, 33}, + {18, 11}, + {15, 11}, + {64, 33}, + {160, 99}, + {4, 3}, + {3, 2}, + {2, 1} +}; + +/***** Utils ****/ +#define EXTENDED_SAR 255 + +static GstH265VPS * +gst_h265_parser_get_vps (GstH265Parser * parser, guint8 vps_id) +{ + GstH265VPS *vps; + + vps = &parser->vps[vps_id]; + + if (vps->valid) + return vps; + + return NULL; +} + +static GstH265SPS * +gst_h265_parser_get_sps (GstH265Parser * parser, guint8 sps_id) +{ + GstH265SPS *sps; + + sps = &parser->sps[sps_id]; + + if (sps->valid) + return sps; + + return NULL; +} + +static GstH265PPS * +gst_h265_parser_get_pps (GstH265Parser * parser, guint8 pps_id) +{ + GstH265PPS *pps; + + pps = &parser->pps[pps_id]; + + if (pps->valid) + return pps; + + return NULL; +} + +static gboolean +gst_h265_parse_nalu_header (GstH265NalUnit * nalu) +{ + guint8 *data = nalu->data + nalu->offset; + GstBitReader br; + + if (nalu->size < 2) + return FALSE; + + gst_bit_reader_init (&br, data, nalu->size - nalu->offset); + + /* skip the forbidden_zero_bit */ + gst_bit_reader_skip_unchecked (&br, 1); + + nalu->type = gst_bit_reader_get_bits_uint8_unchecked (&br, 6); + nalu->layer_id = gst_bit_reader_get_bits_uint8_unchecked (&br, 6); + nalu->temporal_id_plus1 = gst_bit_reader_get_bits_uint8_unchecked (&br, 3); + nalu->header_bytes = 2; + + return TRUE; +} + +struct h265_profile_string +{ + GstH265Profile profile; + const gchar *name; +}; + +static const struct h265_profile_string h265_profiles[] = { + /* keep in sync with definition in the header */ + {GST_H265_PROFILE_MAIN, "main"}, + {GST_H265_PROFILE_MAIN_10, "main-10"}, + {GST_H265_PROFILE_MAIN_STILL_PICTURE, "main-still-picture"}, + {GST_H265_PROFILE_MONOCHROME, "monochrome"}, + {GST_H265_PROFILE_MONOCHROME_12, "monochrome-12"}, + {GST_H265_PROFILE_MONOCHROME_16, "monochrome-16"}, + {GST_H265_PROFILE_MAIN_12, "main-12"}, + {GST_H265_PROFILE_MAIN_422_10, "main-422-10"}, + {GST_H265_PROFILE_MAIN_422_12, "main-422-12"}, + {GST_H265_PROFILE_MAIN_444, "main-444"}, + {GST_H265_PROFILE_MAIN_444_10, "main-444-10"}, + {GST_H265_PROFILE_MAIN_444_12, "main-444-12"}, + {GST_H265_PROFILE_MAIN_INTRA, "main-intra"}, + {GST_H265_PROFILE_MAIN_10_INTRA, "main-10-intra"}, + {GST_H265_PROFILE_MAIN_12_INTRA, "main-12-intra"}, + {GST_H265_PROFILE_MAIN_422_10_INTRA, "main-422-10-intra"}, + {GST_H265_PROFILE_MAIN_422_12_INTRA, "main-422-12-intra"}, + {GST_H265_PROFILE_MAIN_444_INTRA, "main-444-intra"}, + {GST_H265_PROFILE_MAIN_444_10_INTRA, "main-444-10-intra"}, + {GST_H265_PROFILE_MAIN_444_12_INTRA, "main-444-12-intra"}, + {GST_H265_PROFILE_MAIN_444_16_INTRA, "main-444-16-intra"}, + {GST_H265_PROFILE_MAIN_444_STILL_PICTURE, "main-444-still-picture"}, + {GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE, "main-444-16-still-picture"}, + {GST_H265_PROFILE_MONOCHROME_10, "monochrome-10"}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444, "high-throughput-444"}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444_10, "high-throughput-444-10"}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444_14, "high-throughput-444-14"}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA, + "high-throughput-444-16-intra"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN, "screen-extended-main"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10, "screen-extended-main-10"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444, "screen-extended-main-444"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10, "screen-extended-main-444-10"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444, + "screen-extended-high-throughput-444"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10, + "screen-extended-high-throughput-444-10"}, + {GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14, + "screen-extended-high-throughput-444-14"}, + {GST_H265_PROFILE_MULTIVIEW_MAIN, "multiview-main"}, + {GST_H265_PROFILE_SCALABLE_MAIN, "scalable-main"}, + {GST_H265_PROFILE_SCALABLE_MAIN_10, "scalable-main-10"}, + {GST_H265_PROFILE_SCALABLE_MONOCHROME, "scalable-monochrome"}, + {GST_H265_PROFILE_SCALABLE_MONOCHROME_12, "scalable-monochrome-12"}, + {GST_H265_PROFILE_SCALABLE_MONOCHROME_16, "scalable-monochrome-16"}, + {GST_H265_PROFILE_SCALABLE_MAIN_444, "scalable-main-444"}, + {GST_H265_PROFILE_3D_MAIN, "3d-main"}, +}; + +/****** Parsing functions *****/ + +static gboolean +gst_h265_parse_profile_tier_level (GstH265ProfileTierLevel * ptl, + NalReader * nr, guint8 maxNumSubLayersMinus1) +{ + guint i, j; + GST_DEBUG ("parsing \"ProfileTierLevel parameters\""); + + READ_UINT8 (nr, ptl->profile_space, 2); + READ_UINT8 (nr, ptl->tier_flag, 1); + READ_UINT8 (nr, ptl->profile_idc, 5); + + for (j = 0; j < 32; j++) + READ_UINT8 (nr, ptl->profile_compatibility_flag[j], 1); + + READ_UINT8 (nr, ptl->progressive_source_flag, 1); + READ_UINT8 (nr, ptl->interlaced_source_flag, 1); + READ_UINT8 (nr, ptl->non_packed_constraint_flag, 1); + READ_UINT8 (nr, ptl->frame_only_constraint_flag, 1); + + READ_UINT8 (nr, ptl->max_12bit_constraint_flag, 1); + READ_UINT8 (nr, ptl->max_10bit_constraint_flag, 1); + READ_UINT8 (nr, ptl->max_8bit_constraint_flag, 1); + READ_UINT8 (nr, ptl->max_422chroma_constraint_flag, 1); + READ_UINT8 (nr, ptl->max_420chroma_constraint_flag, 1); + READ_UINT8 (nr, ptl->max_monochrome_constraint_flag, 1); + READ_UINT8 (nr, ptl->intra_constraint_flag, 1); + READ_UINT8 (nr, ptl->one_picture_only_constraint_flag, 1); + READ_UINT8 (nr, ptl->lower_bit_rate_constraint_flag, 1); + READ_UINT8 (nr, ptl->max_14bit_constraint_flag, 1); + + /* skip the reserved zero bits */ + if (!nal_reader_skip (nr, 34)) + goto error; + + READ_UINT8 (nr, ptl->level_idc, 8); + for (j = 0; j < maxNumSubLayersMinus1; j++) { + READ_UINT8 (nr, ptl->sub_layer_profile_present_flag[j], 1); + READ_UINT8 (nr, ptl->sub_layer_level_present_flag[j], 1); + } + + if (maxNumSubLayersMinus1 > 0) { + for (i = maxNumSubLayersMinus1; i < 8; i++) + if (!nal_reader_skip (nr, 2)) + goto error; + } + + for (i = 0; i < maxNumSubLayersMinus1; i++) { + if (ptl->sub_layer_profile_present_flag[i]) { + READ_UINT8 (nr, ptl->sub_layer_profile_space[i], 2); + READ_UINT8 (nr, ptl->sub_layer_tier_flag[i], 1); + READ_UINT8 (nr, ptl->sub_layer_profile_idc[i], 5); + + for (j = 0; j < 32; j++) + READ_UINT8 (nr, ptl->sub_layer_profile_compatibility_flag[i][j], 1); + + READ_UINT8 (nr, ptl->sub_layer_progressive_source_flag[i], 1); + READ_UINT8 (nr, ptl->sub_layer_interlaced_source_flag[i], 1); + READ_UINT8 (nr, ptl->sub_layer_non_packed_constraint_flag[i], 1); + READ_UINT8 (nr, ptl->sub_layer_frame_only_constraint_flag[i], 1); + + if (!nal_reader_skip (nr, 44)) + goto error; + } + + if (ptl->sub_layer_level_present_flag[i]) + READ_UINT8 (nr, ptl->sub_layer_level_idc[i], 8); + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"ProfileTierLevel Parameters\""); + return FALSE; +} + +static gboolean +gst_h265_parse_sub_layer_hrd_parameters (GstH265SubLayerHRDParams * sub_hrd, + NalReader * nr, guint8 CpbCnt, guint8 sub_pic_hrd_params_present_flag) +{ + guint i; + + GST_DEBUG ("parsing \"SubLayer HRD Parameters\""); + + for (i = 0; i <= CpbCnt; i++) { + READ_UE_MAX (nr, sub_hrd->bit_rate_value_minus1[i], G_MAXUINT32 - 1); + READ_UE_MAX (nr, sub_hrd->cpb_size_value_minus1[i], G_MAXUINT32 - 1); + + if (sub_pic_hrd_params_present_flag) { + READ_UE_MAX (nr, sub_hrd->cpb_size_du_value_minus1[i], G_MAXUINT32 - 1); + READ_UE_MAX (nr, sub_hrd->bit_rate_du_value_minus1[i], G_MAXUINT32 - 1); + } + + READ_UINT8 (nr, sub_hrd->cbr_flag[i], 1); + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"SubLayerHRD Parameters \""); + return FALSE; +} + +static gboolean +gst_h265_parse_hrd_parameters (GstH265HRDParams * hrd, NalReader * nr, + guint8 commonInfPresentFlag, guint8 maxNumSubLayersMinus1) +{ + guint i; + + GST_DEBUG ("parsing \"HRD Parameters\""); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + hrd->initial_cpb_removal_delay_length_minus1 = 23; + hrd->au_cpb_removal_delay_length_minus1 = 23; + hrd->dpb_output_delay_length_minus1 = 23; + + if (commonInfPresentFlag) { + READ_UINT8 (nr, hrd->nal_hrd_parameters_present_flag, 1); + READ_UINT8 (nr, hrd->vcl_hrd_parameters_present_flag, 1); + + if (hrd->nal_hrd_parameters_present_flag + || hrd->vcl_hrd_parameters_present_flag) { + + READ_UINT8 (nr, hrd->sub_pic_hrd_params_present_flag, 1); + + if (hrd->sub_pic_hrd_params_present_flag) { + READ_UINT8 (nr, hrd->tick_divisor_minus2, 8); + READ_UINT8 (nr, hrd->du_cpb_removal_delay_increment_length_minus1, 5); + READ_UINT8 (nr, hrd->sub_pic_cpb_params_in_pic_timing_sei_flag, 1); + READ_UINT8 (nr, hrd->dpb_output_delay_du_length_minus1, 5); + } + + READ_UINT8 (nr, hrd->bit_rate_scale, 4); + READ_UINT8 (nr, hrd->cpb_size_scale, 4); + + if (hrd->sub_pic_hrd_params_present_flag) + READ_UINT8 (nr, hrd->cpb_size_du_scale, 4); + + READ_UINT8 (nr, hrd->initial_cpb_removal_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->au_cpb_removal_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->dpb_output_delay_length_minus1, 5); + } + } + + for (i = 0; i <= maxNumSubLayersMinus1; i++) { + READ_UINT8 (nr, hrd->fixed_pic_rate_general_flag[i], 1); + + if (!hrd->fixed_pic_rate_general_flag[i]) { + READ_UINT8 (nr, hrd->fixed_pic_rate_within_cvs_flag[i], 1); + } else + hrd->fixed_pic_rate_within_cvs_flag[i] = 1; + + if (hrd->fixed_pic_rate_within_cvs_flag[i]) { + READ_UE_MAX (nr, hrd->elemental_duration_in_tc_minus1[i], 2047); + } else + READ_UINT8 (nr, hrd->low_delay_hrd_flag[i], 1); + + if (!hrd->low_delay_hrd_flag[i]) + READ_UE_MAX (nr, hrd->cpb_cnt_minus1[i], 31); + + if (hrd->nal_hrd_parameters_present_flag) + if (!gst_h265_parse_sub_layer_hrd_parameters (&hrd->sublayer_hrd_params + [i], nr, hrd->cpb_cnt_minus1[i], + hrd->sub_pic_hrd_params_present_flag)) + goto error; + + if (hrd->vcl_hrd_parameters_present_flag) + if (!gst_h265_parse_sub_layer_hrd_parameters (&hrd->sublayer_hrd_params + [i], nr, hrd->cpb_cnt_minus1[i], + hrd->sub_pic_hrd_params_present_flag)) + goto error; + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"HRD Parameters\""); + return FALSE; +} + +static gboolean +gst_h265_parse_vui_parameters (GstH265SPS * sps, NalReader * nr) +{ + GstH265VUIParams *vui = &sps->vui_params; + + GST_DEBUG ("parsing \"VUI Parameters\""); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + vui->video_format = 5; + vui->colour_primaries = 2; + vui->transfer_characteristics = 2; + vui->matrix_coefficients = 2; + vui->motion_vectors_over_pic_boundaries_flag = 1; + vui->max_bytes_per_pic_denom = 2; + vui->max_bits_per_min_cu_denom = 1; + vui->log2_max_mv_length_horizontal = 15; + vui->log2_max_mv_length_vertical = 15; + + if (sps && sps->profile_tier_level.progressive_source_flag + && sps->profile_tier_level.interlaced_source_flag) + vui->frame_field_info_present_flag = 1; + + READ_UINT8 (nr, vui->aspect_ratio_info_present_flag, 1); + if (vui->aspect_ratio_info_present_flag) { + READ_UINT8 (nr, vui->aspect_ratio_idc, 8); + if (vui->aspect_ratio_idc == EXTENDED_SAR) { + READ_UINT16 (nr, vui->sar_width, 16); + READ_UINT16 (nr, vui->sar_height, 16); + vui->par_n = vui->sar_width; + vui->par_d = vui->sar_height; + } else if (vui->aspect_ratio_idc <= 16) { + vui->par_n = aspect_ratios[vui->aspect_ratio_idc].par_n; + vui->par_d = aspect_ratios[vui->aspect_ratio_idc].par_d; + } + } + + READ_UINT8 (nr, vui->overscan_info_present_flag, 1); + if (vui->overscan_info_present_flag) + READ_UINT8 (nr, vui->overscan_appropriate_flag, 1); + + READ_UINT8 (nr, vui->video_signal_type_present_flag, 1); + if (vui->video_signal_type_present_flag) { + + READ_UINT8 (nr, vui->video_format, 3); + READ_UINT8 (nr, vui->video_full_range_flag, 1); + READ_UINT8 (nr, vui->colour_description_present_flag, 1); + if (vui->colour_description_present_flag) { + READ_UINT8 (nr, vui->colour_primaries, 8); + READ_UINT8 (nr, vui->transfer_characteristics, 8); + READ_UINT8 (nr, vui->matrix_coefficients, 8); + } + } + + READ_UINT8 (nr, vui->chroma_loc_info_present_flag, 1); + if (vui->chroma_loc_info_present_flag) { + READ_UE_MAX (nr, vui->chroma_sample_loc_type_top_field, 5); + READ_UE_MAX (nr, vui->chroma_sample_loc_type_bottom_field, 5); + } + + READ_UINT8 (nr, vui->neutral_chroma_indication_flag, 1); + READ_UINT8 (nr, vui->field_seq_flag, 1); + READ_UINT8 (nr, vui->frame_field_info_present_flag, 1); + + READ_UINT8 (nr, vui->default_display_window_flag, 1); + if (vui->default_display_window_flag) { + READ_UE (nr, vui->def_disp_win_left_offset); + READ_UE (nr, vui->def_disp_win_right_offset); + READ_UE (nr, vui->def_disp_win_top_offset); + READ_UE (nr, vui->def_disp_win_bottom_offset); + } + + READ_UINT8 (nr, vui->timing_info_present_flag, 1); + if (vui->timing_info_present_flag) { + READ_UINT32 (nr, vui->num_units_in_tick, 32); + if (vui->num_units_in_tick == 0) + GST_WARNING ("num_units_in_tick = 0 detected in stream " + "(incompliant to H.265 E.2.1)."); + + READ_UINT32 (nr, vui->time_scale, 32); + if (vui->time_scale == 0) + GST_WARNING ("time_scale = 0 detected in stream " + "(incompliant to H.265 E.2.1)."); + + READ_UINT8 (nr, vui->poc_proportional_to_timing_flag, 1); + if (vui->poc_proportional_to_timing_flag) + READ_UE_MAX (nr, vui->num_ticks_poc_diff_one_minus1, G_MAXUINT32 - 1); + + READ_UINT8 (nr, vui->hrd_parameters_present_flag, 1); + if (vui->hrd_parameters_present_flag) + if (!gst_h265_parse_hrd_parameters (&vui->hrd_params, nr, 1, + sps->max_sub_layers_minus1)) + goto error; + } + + READ_UINT8 (nr, vui->bitstream_restriction_flag, 1); + if (vui->bitstream_restriction_flag) { + READ_UINT8 (nr, vui->tiles_fixed_structure_flag, 1); + READ_UINT8 (nr, vui->motion_vectors_over_pic_boundaries_flag, 1); + READ_UINT8 (nr, vui->restricted_ref_pic_lists_flag, 1); + READ_UE_MAX (nr, vui->min_spatial_segmentation_idc, 4096); + READ_UE_MAX (nr, vui->max_bytes_per_pic_denom, 16); + READ_UE_MAX (nr, vui->max_bits_per_min_cu_denom, 16); + READ_UE_MAX (nr, vui->log2_max_mv_length_horizontal, 16); + READ_UE_MAX (nr, vui->log2_max_mv_length_vertical, 15); + } + + vui->parsed = TRUE; + return TRUE; + +error: + GST_WARNING ("error parsing \"VUI Parameters\""); + vui->parsed = FALSE; + return FALSE; +} + +static gboolean +get_scaling_list_params (GstH265ScalingList * dest_scaling_list, + guint8 sizeId, guint8 matrixId, guint8 ** sl, guint8 * size, + gint16 ** scaling_list_dc_coef_minus8) +{ + switch (sizeId) { + case GST_H265_QUANT_MATIX_4X4: + *sl = dest_scaling_list->scaling_lists_4x4[matrixId]; + if (size) + *size = 16; + break; + case GST_H265_QUANT_MATIX_8X8: + *sl = dest_scaling_list->scaling_lists_8x8[matrixId]; + if (size) + *size = 64; + break; + case GST_H265_QUANT_MATIX_16X16: + *sl = dest_scaling_list->scaling_lists_16x16[matrixId]; + if (size) + *size = 64; + if (scaling_list_dc_coef_minus8) + *scaling_list_dc_coef_minus8 = + dest_scaling_list->scaling_list_dc_coef_minus8_16x16; + break; + case GST_H265_QUANT_MATIX_32X32: + *sl = dest_scaling_list->scaling_lists_32x32[matrixId]; + if (size) + *size = 64; + if (scaling_list_dc_coef_minus8) + *scaling_list_dc_coef_minus8 = + dest_scaling_list->scaling_list_dc_coef_minus8_32x32; + break; + default: + return FALSE; + } + return TRUE; +} + +static gboolean +get_default_scaling_lists (guint8 ** sl, guint8 sizeId, guint8 matrixId) +{ + switch (sizeId) { + case GST_H265_QUANT_MATIX_4X4: + memcpy (*sl, default_scaling_list0, 16); + break; + + case GST_H265_QUANT_MATIX_8X8: + case GST_H265_QUANT_MATIX_16X16: + if (matrixId <= 2) + memcpy (*sl, default_scaling_list1, 64); + else + memcpy (*sl, default_scaling_list2, 64); + break; + + case GST_H265_QUANT_MATIX_32X32: + if (matrixId == 0) + memcpy (*sl, default_scaling_list1, 64); + else + memcpy (*sl, default_scaling_list2, 64); + break; + + default: + return FALSE; + break; + } + return TRUE; +} + +static gboolean +gst_h265_parser_parse_scaling_lists (NalReader * nr, + GstH265ScalingList * dest_scaling_list, gboolean use_default) +{ + guint8 sizeId; + guint8 matrixId; + guint8 scaling_list_pred_mode_flag = 0; + guint8 scaling_list_pred_matrix_id_delta = 0; + guint8 size, i; + + GST_DEBUG ("parsing scaling lists"); + + for (sizeId = 0; sizeId < 4; sizeId++) { + for (matrixId = 0; matrixId < ((sizeId == 3) ? 2 : 6); matrixId++) { + gint16 *scaling_list_dc_coef_minus8 = NULL; + guint8 *sl; + + if (!get_scaling_list_params (dest_scaling_list, sizeId, matrixId, &sl, + &size, &scaling_list_dc_coef_minus8)) + goto error; + + /* use_default_scaling_matrices forcefully which means, + * sps_scaling_list_enabled_flag=TRUE, + * sps_scaling_list_data_present_flag=FALSE, + * pps_scaling_list_data_present_falg=FALSE */ + if (use_default) { + if (!get_default_scaling_lists (&sl, sizeId, matrixId)) + goto error; + + /* Inferring the value of scaling_list_dc_coef_minus8 */ + if (sizeId > 1) + scaling_list_dc_coef_minus8[matrixId] = 8; + + } else { + READ_UINT8 (nr, scaling_list_pred_mode_flag, 1); + + if (!scaling_list_pred_mode_flag) { + guint8 refMatrixId; + + READ_UE_MAX (nr, scaling_list_pred_matrix_id_delta, matrixId); + + if (!scaling_list_pred_matrix_id_delta) { + if (!get_default_scaling_lists (&sl, sizeId, matrixId)) + goto error; + + /* Inferring the value of scaling_list_dc_coef_minus8 */ + if (sizeId > 1) + scaling_list_dc_coef_minus8[matrixId] = 8; + + } else { + guint8 *temp_sl; + + refMatrixId = matrixId - scaling_list_pred_matrix_id_delta; /* 7-30 */ + + if (!get_scaling_list_params (dest_scaling_list, sizeId, + refMatrixId, &temp_sl, NULL, NULL)) + goto error; + + for (i = 0; i < size; i++) + sl[i] = temp_sl[i]; /* 7-31 */ + + + /* Inferring the value of scaling_list_dc_coef_minus8 */ + if (sizeId > 1) + scaling_list_dc_coef_minus8[matrixId] = + scaling_list_dc_coef_minus8[refMatrixId]; + } + } else { + guint8 nextCoef = 8; + gint8 scaling_list_delta_coef; + + if (sizeId > 1) { + READ_SE_ALLOWED (nr, scaling_list_dc_coef_minus8[matrixId], -7, + 247); + nextCoef = scaling_list_dc_coef_minus8[matrixId] + 8; + } + + for (i = 0; i < size; i++) { + READ_SE_ALLOWED (nr, scaling_list_delta_coef, -128, 127); + nextCoef = (nextCoef + scaling_list_delta_coef) & 0xff; + sl[i] = nextCoef; + } + } + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing scaling lists"); + return FALSE; +} + +static gboolean +gst_h265_parser_parse_short_term_ref_pic_sets (GstH265ShortTermRefPicSet * + stRPS, NalReader * nr, guint8 stRpsIdx, GstH265SPS * sps, + GstH265SPSEXT * sps_ext) +{ + guint8 num_short_term_ref_pic_sets; + guint8 RefRpsIdx = 0; + gint16 deltaRps = 0; + gint j, i = 0; + gint dPoc; + + GstH265ShortTermRefPicSetExt *stRPSEXT = + &sps_ext->short_term_ref_pic_set_ext[stRpsIdx]; + + GST_DEBUG ("parsing \"ShortTermRefPicSetParameters\""); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + for (j = 0; j < 16; j++) + stRPSEXT->use_delta_flag[j] = 1; + + num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; + + if (stRpsIdx != 0) + READ_UINT8 (nr, stRPS->inter_ref_pic_set_prediction_flag, 1); + + if (stRPS->inter_ref_pic_set_prediction_flag) { + GstH265ShortTermRefPicSet *RefRPS; + + if (stRpsIdx == num_short_term_ref_pic_sets) + READ_UE_MAX (nr, stRPS->delta_idx_minus1, stRpsIdx - 1); + + READ_UINT8 (nr, stRPS->delta_rps_sign, 1); + READ_UE_MAX (nr, stRPS->abs_delta_rps_minus1, 32767); + + RefRpsIdx = stRpsIdx - stRPS->delta_idx_minus1 - 1; /* 7-45 */ + deltaRps = (1 - 2 * stRPS->delta_rps_sign) * (stRPS->abs_delta_rps_minus1 + 1); /* 7-46 */ + + RefRPS = &sps->short_term_ref_pic_set[RefRpsIdx]; + stRPS->NumDeltaPocsOfRefRpsIdx = RefRPS->NumDeltaPocs; + + for (j = 0; j <= RefRPS->NumDeltaPocs; j++) { + READ_UINT8 (nr, stRPSEXT->used_by_curr_pic_flag[j], 1); + if (!stRPSEXT->used_by_curr_pic_flag[j]) + READ_UINT8 (nr, stRPSEXT->use_delta_flag[j], 1); + } + + /* 7-47: calculate NumNegativePics, DeltaPocS0 and UsedByCurrPicS0 */ + i = 0; + for (j = (RefRPS->NumPositivePics - 1); j >= 0; j--) { + dPoc = RefRPS->DeltaPocS1[j] + deltaRps; + if (dPoc < 0 && stRPSEXT->use_delta_flag[RefRPS->NumNegativePics + j]) { + stRPS->DeltaPocS0[i] = dPoc; + stRPS->UsedByCurrPicS0[i++] = + stRPSEXT->used_by_curr_pic_flag[RefRPS->NumNegativePics + j]; + } + } + if (deltaRps < 0 && stRPSEXT->use_delta_flag[RefRPS->NumDeltaPocs]) { + stRPS->DeltaPocS0[i] = deltaRps; + stRPS->UsedByCurrPicS0[i++] = + stRPSEXT->used_by_curr_pic_flag[RefRPS->NumDeltaPocs]; + } + for (j = 0; j < RefRPS->NumNegativePics; j++) { + dPoc = RefRPS->DeltaPocS0[j] + deltaRps; + if (dPoc < 0 && stRPSEXT->use_delta_flag[j]) { + stRPS->DeltaPocS0[i] = dPoc; + stRPS->UsedByCurrPicS0[i++] = stRPSEXT->used_by_curr_pic_flag[j]; + } + } + stRPS->NumNegativePics = i; + + /* 7-48: calculate NumPositivePics, DeltaPocS1 and UsedByCurrPicS1 */ + i = 0; + for (j = (RefRPS->NumNegativePics - 1); j >= 0; j--) { + dPoc = RefRPS->DeltaPocS0[j] + deltaRps; + if (dPoc > 0 && stRPSEXT->use_delta_flag[j]) { + stRPS->DeltaPocS1[i] = dPoc; + stRPS->UsedByCurrPicS1[i++] = stRPSEXT->used_by_curr_pic_flag[j]; + } + } + if (deltaRps > 0 && stRPSEXT->use_delta_flag[RefRPS->NumDeltaPocs]) { + stRPS->DeltaPocS1[i] = deltaRps; + stRPS->UsedByCurrPicS1[i++] = + stRPSEXT->used_by_curr_pic_flag[RefRPS->NumDeltaPocs]; + } + for (j = 0; j < RefRPS->NumPositivePics; j++) { + dPoc = RefRPS->DeltaPocS1[j] + deltaRps; + if (dPoc > 0 && stRPSEXT->use_delta_flag[RefRPS->NumNegativePics + j]) { + stRPS->DeltaPocS1[i] = dPoc; + stRPS->UsedByCurrPicS1[i++] = + stRPSEXT->used_by_curr_pic_flag[RefRPS->NumNegativePics + j]; + } + } + stRPS->NumPositivePics = i; + + } else { + /* 7-49 */ + READ_UE_MAX (nr, stRPS->NumNegativePics, + sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1]); + + /* 7-50 */ + READ_UE_MAX (nr, stRPS->NumPositivePics, + (sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1] - + stRPS->NumNegativePics)); + + for (i = 0; i < stRPS->NumNegativePics; i++) { + READ_UE_MAX (nr, stRPSEXT->delta_poc_s0_minus1[i], 32767); + /* 7-51 */ + READ_UINT8 (nr, stRPS->UsedByCurrPicS0[i], 1); + stRPSEXT->used_by_curr_pic_flag[i] = stRPS->UsedByCurrPicS0[i]; + + if (i == 0) { + /* 7-53 */ + stRPS->DeltaPocS0[i] = -(stRPSEXT->delta_poc_s0_minus1[i] + 1); + } else { + /* 7-55 */ + stRPS->DeltaPocS0[i] = + stRPS->DeltaPocS0[i - 1] - (stRPSEXT->delta_poc_s0_minus1[i] + 1); + } + } + + for (j = 0; j < stRPS->NumPositivePics; j++) { + READ_UE_MAX (nr, stRPSEXT->delta_poc_s1_minus1[j], 32767); + + /* 7-52 */ + READ_UINT8 (nr, stRPS->UsedByCurrPicS1[j], 1); + stRPSEXT->used_by_curr_pic_flag[i + j] = stRPS->UsedByCurrPicS1[j]; + + if (j == 0) { + /* 7-54 */ + stRPS->DeltaPocS1[j] = stRPSEXT->delta_poc_s1_minus1[j] + 1; + } else { + /* 7-56 */ + stRPS->DeltaPocS1[j] = + stRPS->DeltaPocS1[j - 1] + (stRPSEXT->delta_poc_s1_minus1[j] + 1); + } + } + + } + + /* 7-57 */ + stRPS->NumDeltaPocs = stRPS->NumPositivePics + stRPS->NumNegativePics; + + return TRUE; + +error: + GST_WARNING ("error parsing \"ShortTermRefPicSet Parameters\""); + return FALSE; +} + +static gboolean +gst_h265_slice_parse_ref_pic_list_modification (GstH265SliceHdr * slice, + NalReader * nr, gint NumPocTotalCurr) +{ + guint i; + GstH265RefPicListModification *rpl_mod = &slice->ref_pic_list_modification; + const guint n = gst_util_ceil_log2 (NumPocTotalCurr); + + READ_UINT8 (nr, rpl_mod->ref_pic_list_modification_flag_l0, 1); + + if (rpl_mod->ref_pic_list_modification_flag_l0) { + for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) { + /* 7.4.7.2 list_entry_l0 */ + READ_UINT32 (nr, rpl_mod->list_entry_l0[i], n); + CHECK_ALLOWED_MAX (rpl_mod->list_entry_l0[i], (NumPocTotalCurr - 1)); + } + } + if (GST_H265_IS_B_SLICE (slice)) { + READ_UINT8 (nr, rpl_mod->ref_pic_list_modification_flag_l1, 1); + if (rpl_mod->ref_pic_list_modification_flag_l1) + for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) { + READ_UINT32 (nr, rpl_mod->list_entry_l1[i], n); + CHECK_ALLOWED_MAX (rpl_mod->list_entry_l1[i], (NumPocTotalCurr - 1)); + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Reference picture list modifications\""); + return FALSE; +} + +static gboolean +gst_h265_slice_parse_pred_weight_table (GstH265SliceHdr * slice, NalReader * nr) +{ + GstH265PredWeightTable *p; + gint i, j; + GstH265PPS *pps = slice->pps; + GstH265SPS *sps = pps->sps; + + GST_DEBUG ("parsing \"Prediction weight table\""); + + p = &slice->pred_weight_table; + + READ_UE_MAX (nr, p->luma_log2_weight_denom, 7); + + if (sps->chroma_format_idc != 0) { + READ_SE_ALLOWED (nr, p->delta_chroma_log2_weight_denom, + (0 - p->luma_log2_weight_denom), (7 - p->luma_log2_weight_denom)); + } + + for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) + READ_UINT8 (nr, p->luma_weight_l0_flag[i], 1); + + if (sps->chroma_format_idc != 0) + for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) + READ_UINT8 (nr, p->chroma_weight_l0_flag[i], 1); + + for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) { + if (p->luma_weight_l0_flag[i]) { + READ_SE_ALLOWED (nr, p->delta_luma_weight_l0[i], -128, 127); + READ_SE_ALLOWED (nr, p->luma_offset_l0[i], -128, 127); + } + if (p->chroma_weight_l0_flag[i]) + for (j = 0; j < 2; j++) { + READ_SE_ALLOWED (nr, p->delta_chroma_weight_l0[i][j], -128, 127); + READ_SE_ALLOWED (nr, p->delta_chroma_offset_l0[i][j], -512, 511); + } + } + + if (GST_H265_IS_B_SLICE (slice)) { + for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) + READ_UINT8 (nr, p->luma_weight_l1_flag[i], 1); + if (sps->chroma_format_idc != 0) + for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) + READ_UINT8 (nr, p->chroma_weight_l1_flag[i], 1); + + for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) { + if (p->luma_weight_l1_flag[i]) { + READ_SE_ALLOWED (nr, p->delta_luma_weight_l1[i], -128, 127); + READ_SE_ALLOWED (nr, p->luma_offset_l1[i], -128, 127); + } + if (p->chroma_weight_l1_flag[i]) + for (j = 0; j < 2; j++) { + READ_SE_ALLOWED (nr, p->delta_chroma_weight_l1[i][j], -128, 127); + READ_SE_ALLOWED (nr, p->delta_chroma_offset_l1[i][j], -512, 511); + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Prediction weight table\""); + return FALSE; +} + +static GstH265ParserResult +gst_h265_parser_parse_buffering_period (GstH265Parser * parser, + GstH265BufferingPeriod * per, NalReader * nr) +{ + GstH265SPS *sps; + guint8 sps_id; + guint i; + guint n; + + GST_DEBUG ("parsing \"Buffering period\""); + + READ_UE_MAX (nr, sps_id, GST_H265_MAX_SPS_COUNT - 1); + sps = gst_h265_parser_get_sps (parser, sps_id); + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + sps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + per->sps = sps; + + if (sps->vui_parameters_present_flag) { + GstH265VUIParams *vui = &sps->vui_params; + GstH265HRDParams *hrd = &vui->hrd_params; + + if (!hrd->sub_pic_hrd_params_present_flag) + READ_UINT8 (nr, per->irap_cpb_params_present_flag, 1); + + if (per->irap_cpb_params_present_flag) { + READ_UINT8 (nr, per->cpb_delay_offset, + (hrd->au_cpb_removal_delay_length_minus1 + 1)); + READ_UINT8 (nr, per->dpb_delay_offset, + (hrd->dpb_output_delay_length_minus1 + 1)); + } + + n = hrd->initial_cpb_removal_delay_length_minus1 + 1; + + READ_UINT8 (nr, per->concatenation_flag, 1); + READ_UINT8 (nr, per->au_cpb_removal_delay_delta_minus1, + (hrd->au_cpb_removal_delay_length_minus1 + 1)); + + if (hrd->nal_hrd_parameters_present_flag) { + for (i = 0; i <= hrd->cpb_cnt_minus1[i]; i++) { + READ_UINT8 (nr, per->nal_initial_cpb_removal_delay[i], n); + READ_UINT8 (nr, per->nal_initial_cpb_removal_offset[i], n); + if (hrd->sub_pic_hrd_params_present_flag + || per->irap_cpb_params_present_flag) { + READ_UINT8 (nr, per->nal_initial_alt_cpb_removal_delay[i], n); + READ_UINT8 (nr, per->nal_initial_alt_cpb_removal_offset[i], n); + } + } + } + + if (hrd->vcl_hrd_parameters_present_flag) { + for (i = 0; i <= hrd->cpb_cnt_minus1[i]; i++) { + READ_UINT8 (nr, per->vcl_initial_cpb_removal_delay[i], n); + READ_UINT8 (nr, per->vcl_initial_cpb_removal_offset[i], n); + if (hrd->sub_pic_hrd_params_present_flag + || per->irap_cpb_params_present_flag) { + READ_UINT8 (nr, per->vcl_initial_alt_cpb_removal_delay[i], n); + READ_UINT8 (nr, per->vcl_initial_alt_cpb_removal_offset[i], n); + } + } + } + + } + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Buffering period\""); + return GST_H265_PARSER_ERROR; +} + +static GstH265ParserResult +gst_h265_parser_parse_pic_timing (GstH265Parser * parser, + GstH265PicTiming * tim, NalReader * nr) +{ + GstH265ProfileTierLevel *profile_tier_level; + guint i; + + GST_DEBUG ("parsing \"Picture timing\""); + if (!parser->last_sps || !parser->last_sps->valid) { + GST_WARNING ("didn't get the associated sequence parameter set for the " + "current access unit"); + goto error; + } + + profile_tier_level = &parser->last_sps->profile_tier_level; + + /* set default values */ + if (!profile_tier_level->progressive_source_flag + && profile_tier_level->interlaced_source_flag) + tim->source_scan_type = 0; + else if (profile_tier_level->progressive_source_flag + && !profile_tier_level->interlaced_source_flag) + tim->source_scan_type = 1; + else + tim->source_scan_type = 2; + + if (parser->last_sps->vui_parameters_present_flag) { + GstH265VUIParams *vui = &parser->last_sps->vui_params; + + if (vui->frame_field_info_present_flag) { + READ_UINT8 (nr, tim->pic_struct, 4); + READ_UINT8 (nr, tim->source_scan_type, 2); + READ_UINT8 (nr, tim->duplicate_flag, 1); + } else { + /* set default values */ + tim->pic_struct = 0; + } + + if (vui->hrd_parameters_present_flag) { + GstH265HRDParams *hrd = &vui->hrd_params; + + READ_UINT8 (nr, tim->au_cpb_removal_delay_minus1, + (hrd->au_cpb_removal_delay_length_minus1 + 1)); + READ_UINT8 (nr, tim->pic_dpb_output_delay, + (hrd->dpb_output_delay_length_minus1 + 1)); + + if (hrd->sub_pic_hrd_params_present_flag) + READ_UINT8 (nr, tim->pic_dpb_output_du_delay, + (hrd->dpb_output_delay_du_length_minus1 + 1)); + + if (hrd->sub_pic_hrd_params_present_flag + && hrd->sub_pic_cpb_params_in_pic_timing_sei_flag) { + READ_UE_MAX (nr, tim->num_decoding_units_minus1, + GST_H265_MAX_SLICE_SEGMENTS_PER_PICTURE - 1); + + READ_UINT8 (nr, tim->du_common_cpb_removal_delay_flag, 1); + if (tim->du_common_cpb_removal_delay_flag) + READ_UINT8 (nr, tim->du_common_cpb_removal_delay_increment_minus1, + (hrd->du_cpb_removal_delay_increment_length_minus1 + 1)); + + tim->num_nalus_in_du_minus1 = + g_new0 (guint32, (tim->num_decoding_units_minus1 + 1)); + tim->du_cpb_removal_delay_increment_minus1 = + g_new0 (guint8, (tim->num_decoding_units_minus1 + 1)); + + for (i = 0; i <= tim->num_decoding_units_minus1; i++) { + READ_UE (nr, tim->num_nalus_in_du_minus1[i]); + + if (!tim->du_common_cpb_removal_delay_flag + && (i < tim->num_decoding_units_minus1)) + READ_UINT8 (nr, tim->du_cpb_removal_delay_increment_minus1[i], + (hrd->du_cpb_removal_delay_increment_length_minus1 + 1)); + } + } + } + } + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Picture timing\""); + return GST_H265_PARSER_ERROR; +} + +static GstH265ParserResult +gst_h265_parser_parse_recovery_point (GstH265Parser * parser, + GstH265RecoveryPoint * rp, NalReader * nr) +{ + GstH265SPS *const sps = parser->last_sps; + gint32 max_pic_order_cnt_lsb; + + GST_DEBUG ("parsing \"Recovery point\""); + if (!sps || !sps->valid) { + GST_WARNING ("didn't get the associated sequence parameter set for the " + "current access unit"); + goto error; + } + + max_pic_order_cnt_lsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + READ_SE_ALLOWED (nr, rp->recovery_poc_cnt, -max_pic_order_cnt_lsb / 2, + max_pic_order_cnt_lsb - 1); + READ_UINT8 (nr, rp->exact_match_flag, 1); + READ_UINT8 (nr, rp->broken_link_flag, 1); + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Recovery point\""); + return GST_H265_PARSER_ERROR; +} + + +static GstH265ParserResult +gst_h265_parser_parse_registered_user_data (GstH265Parser * parser, + GstH265RegisteredUserData * rud, NalReader * nr, guint payload_size) +{ + guint8 *data = NULL; + guint i; + + rud->data = NULL; + rud->size = 0; + + if (payload_size < 2) { + GST_WARNING ("Too small payload size %d", payload_size); + return GST_H265_PARSER_BROKEN_DATA; + } + + READ_UINT8 (nr, rud->country_code, 8); + --payload_size; + + if (rud->country_code == 0xFF) { + READ_UINT8 (nr, rud->country_code_extension, 8); + --payload_size; + } else { + rud->country_code_extension = 0; + } + + if (payload_size < 1) { + GST_WARNING ("No more remaining payload data to store"); + return GST_H265_PARSER_BROKEN_DATA; + } + + data = g_malloc (payload_size); + for (i = 0; i < payload_size; ++i) { + READ_UINT8 (nr, data[i], 8); + } + + GST_MEMDUMP ("SEI user data", data, payload_size); + + rud->data = data; + rud->size = payload_size; + return GST_H265_PARSER_OK; + +error: + { + GST_WARNING ("error parsing \"Registered User Data\""); + g_free (data); + return GST_H265_PARSER_ERROR; + } +} + +static GstH265ParserResult +gst_h265_parser_parse_user_data_unregistered (GstH265Parser * parser, + GstH265UserDataUnregistered * urud, NalReader * nr, guint payload_size) +{ + guint8 *data = NULL; + gint i; + + if (payload_size < 16) { + GST_WARNING ("Too small payload size %d", payload_size); + return GST_H265_PARSER_BROKEN_DATA; + } + + for (int i = 0; i < 16; i++) { + READ_UINT8 (nr, urud->uuid[i], 8); + } + payload_size -= 16; + + urud->size = payload_size; + + data = g_malloc0 (payload_size); + for (i = 0; i < payload_size; ++i) { + READ_UINT8 (nr, data[i], 8); + } + + urud->data = data; + GST_MEMDUMP ("SEI user data unregistered", data, payload_size); + return GST_H265_PARSER_OK; + +error: + { + GST_WARNING ("error parsing \"User Data Unregistered\""); + g_clear_pointer (&data, g_free); + return GST_H265_PARSER_ERROR; + } +} + +static GstH265ParserResult +gst_h265_parser_parse_time_code (GstH265Parser * parser, + GstH265TimeCode * tc, NalReader * nr) +{ + guint i; + + GST_DEBUG ("parsing \"Time code\""); + + READ_UINT8 (nr, tc->num_clock_ts, 2); + + for (i = 0; i < tc->num_clock_ts; i++) { + READ_UINT8 (nr, tc->clock_timestamp_flag[i], 1); + if (tc->clock_timestamp_flag[i]) { + READ_UINT8 (nr, tc->units_field_based_flag[i], 1); + READ_UINT8 (nr, tc->counting_type[i], 5); + READ_UINT8 (nr, tc->full_timestamp_flag[i], 1); + READ_UINT8 (nr, tc->discontinuity_flag[i], 1); + READ_UINT8 (nr, tc->cnt_dropped_flag[i], 1); + READ_UINT16 (nr, tc->n_frames[i], 9); + + if (tc->full_timestamp_flag[i]) { + tc->seconds_flag[i] = TRUE; + READ_UINT8 (nr, tc->seconds_value[i], 6); + + tc->minutes_flag[i] = TRUE; + READ_UINT8 (nr, tc->minutes_value[i], 6); + + tc->hours_flag[i] = TRUE; + READ_UINT8 (nr, tc->hours_value[i], 5); + } else { + READ_UINT8 (nr, tc->seconds_flag[i], 1); + if (tc->seconds_flag[i]) { + READ_UINT8 (nr, tc->seconds_value[i], 6); + READ_UINT8 (nr, tc->minutes_flag[i], 1); + if (tc->minutes_flag[i]) { + READ_UINT8 (nr, tc->minutes_value[i], 6); + READ_UINT8 (nr, tc->hours_flag[i], 1); + if (tc->hours_flag[i]) { + READ_UINT8 (nr, tc->hours_value[i], 5); + } + } + } + } + } + + READ_UINT8 (nr, tc->time_offset_length[i], 5); + + if (tc->time_offset_length[i] > 0) + READ_UINT32 (nr, tc->time_offset_value[i], tc->time_offset_length[i]); + } + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Time code\""); + return GST_H265_PARSER_ERROR; +} + +static GstH265ParserResult +gst_h265_parser_parse_mastering_display_colour_volume (GstH265Parser * parser, + GstH265MasteringDisplayColourVolume * mdcv, NalReader * nr) +{ + guint i; + + GST_DEBUG ("parsing \"Mastering display colour volume\""); + + for (i = 0; i < 3; i++) { + READ_UINT16 (nr, mdcv->display_primaries_x[i], 16); + READ_UINT16 (nr, mdcv->display_primaries_y[i], 16); + } + + READ_UINT16 (nr, mdcv->white_point_x, 16); + READ_UINT16 (nr, mdcv->white_point_y, 16); + READ_UINT32 (nr, mdcv->max_display_mastering_luminance, 32); + READ_UINT32 (nr, mdcv->min_display_mastering_luminance, 32); + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Mastering display colour volume\""); + return GST_H265_PARSER_ERROR; +} + +static GstH265ParserResult +gst_h265_parser_parse_content_light_level_info (GstH265Parser * parser, + GstH265ContentLightLevel * cll, NalReader * nr) +{ + GST_DEBUG ("parsing \"Content light level\""); + + READ_UINT16 (nr, cll->max_content_light_level, 16); + READ_UINT16 (nr, cll->max_pic_average_light_level, 16); + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Content light level\""); + return GST_H265_PARSER_ERROR; +} + +/******** API *************/ + +/** + * gst_h265_parser_new: (skip) + * + * Creates a new #GstH265Parser. It should be freed with + * gst_h265_parser_free after use. + * + * Returns: a new #GstH265Parser + */ +GstH265Parser * +gst_h265_parser_new (void) +{ + GstH265Parser *parser; + + parser = g_new0 (GstH265Parser, 1); + + return parser; +} + +/** + * gst_h265_parser_free: + * @parser: the #GstH265Parser to free + * + * Frees @parser + */ +void +gst_h265_parser_free (GstH265Parser * parser) +{ + g_free (parser); +} + +/** + * gst_h265_parser_identify_nalu_unchecked: + * @parser: a #GstH265Parser + * @data: The data to parse + * @offset: the offset from which to parse @data + * @size: the size of @data + * @nalu: The #GstH265NalUnit where to store parsed nal headers + * + * Parses @data and fills @nalu from the next nalu data from @data. + * + * This differs from @gst_h265_parser_identify_nalu in that it doesn't + * check whether the packet is complete or not. + * + * Note: Only use this function if you already know the provided @data + * is a complete NALU, else use @gst_h265_parser_identify_nalu. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_identify_nalu_unchecked (GstH265Parser * parser, + const guint8 * data, guint offset, gsize size, GstH265NalUnit * nalu) +{ + gint off1; + + memset (nalu, 0, sizeof (*nalu)); + + if (size < offset + 4) { + GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT + ", offset %u", size, offset); + return GST_H265_PARSER_ERROR; + } + + off1 = scan_for_start_codes (data + offset, size - offset); + + if (off1 < 0) { + GST_DEBUG ("No start code prefix in this buffer"); + return GST_H265_PARSER_NO_NAL; + } + + nalu->sc_offset = offset + off1; + + /* The scanner ensures one byte passed the start code but to + * identify an HEVC NAL, we need 2. */ + if (size - nalu->sc_offset - 3 < 2) { + GST_DEBUG ("Not enough bytes after start code to identify"); + return GST_H265_PARSER_NO_NAL; + } + + /* sc might have 2 or 3 0-bytes */ + if (nalu->sc_offset > 0 && data[nalu->sc_offset - 1] == 00) + nalu->sc_offset--; + + nalu->offset = offset + off1 + 3; + nalu->data = (guint8 *) data; + nalu->size = size - nalu->offset; + + if (!gst_h265_parse_nalu_header (nalu)) { + GST_WARNING ("error parsing \"NAL unit header\""); + nalu->size = 0; + return GST_H265_PARSER_BROKEN_DATA; + } + + nalu->valid = TRUE; + + if (nalu->type == GST_H265_NAL_EOS || nalu->type == GST_H265_NAL_EOB) { + GST_DEBUG ("end-of-seq or end-of-stream nal found"); + nalu->size = 2; + return GST_H265_PARSER_OK; + } + + return GST_H265_PARSER_OK; +} + +/** + * gst_h265_parser_identify_nalu: + * @parser: a #GstH265Parser + * @data: The data to parse + * @offset: the offset from which to parse @data + * @size: the size of @data + * @nalu: The #GstH265NalUnit where to store parsed nal headers + * + * Parses @data and fills @nalu from the next nalu data from @data + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_identify_nalu (GstH265Parser * parser, + const guint8 * data, guint offset, gsize size, GstH265NalUnit * nalu) +{ + GstH265ParserResult res; + gint off2; + + res = + gst_h265_parser_identify_nalu_unchecked (parser, data, offset, size, + nalu); + + if (res != GST_H265_PARSER_OK) + goto beach; + + /* The two NALs are exactly 2 bytes size and are placed at the end of an AU, + * there is no need to wait for the following */ + if (nalu->type == GST_H265_NAL_EOS || nalu->type == GST_H265_NAL_EOB) + goto beach; + + off2 = scan_for_start_codes (data + nalu->offset, size - nalu->offset); + if (off2 < 0) { + GST_DEBUG ("Nal start %d, No end found", nalu->offset); + + return GST_H265_PARSER_NO_NAL_END; + } + + /* Callers assumes that enough data will available to identify the next NAL, + * but scan_for_start_codes() only ensure 1 extra byte is available. Ensure + * we have the required two header bytes (3 bytes start code and 2 byte + * header). */ + if (size - (nalu->offset + off2) < 5) { + GST_DEBUG ("Not enough bytes identify the next NAL."); + return GST_H265_PARSER_NO_NAL_END; + } + + /* Mini performance improvement: + * We could have a way to store how many 0s were skipped to avoid + * parsing them again on the next NAL */ + while (off2 > 0 && data[nalu->offset + off2 - 1] == 00) + off2--; + + nalu->size = off2; + if (nalu->size < 3) + return GST_H265_PARSER_BROKEN_DATA; + + GST_DEBUG ("Complete nal found. Off: %d, Size: %d", nalu->offset, nalu->size); + +beach: + return res; +} + +/** + * gst_h265_parser_identify_nalu_hevc: + * @parser: a #GstH265Parser + * @data: The data to parse, must be the beging of the Nal unit + * @offset: the offset from which to parse @data + * @size: the size of @data + * @nal_length_size: the size in bytes of the HEVC nal length prefix. + * @nalu: The #GstH265NalUnit where to store parsed nal headers + * + * Parses @data and sets @nalu. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_identify_nalu_hevc (GstH265Parser * parser, + const guint8 * data, guint offset, gsize size, guint8 nal_length_size, + GstH265NalUnit * nalu) +{ + GstBitReader br; + + memset (nalu, 0, sizeof (*nalu)); + + /* Would overflow guint below otherwise: the callers needs to ensure that + * this never happens */ + if (offset > G_MAXUINT32 - nal_length_size) { + GST_WARNING ("offset + nal_length_size overflow"); + nalu->size = 0; + return GST_H265_PARSER_BROKEN_DATA; + } + + if (size < offset + nal_length_size) { + GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT + ", offset %u", size, offset); + return GST_H265_PARSER_ERROR; + } + + size = size - offset; + gst_bit_reader_init (&br, data + offset, size); + + nalu->size = gst_bit_reader_get_bits_uint32_unchecked (&br, + nal_length_size * 8); + nalu->sc_offset = offset; + nalu->offset = offset + nal_length_size; + + if (nalu->size > G_MAXUINT32 - nal_length_size) { + GST_WARNING ("NALU size + nal_length_size overflow"); + nalu->size = 0; + return GST_H265_PARSER_BROKEN_DATA; + } + + if (size < (gsize) nalu->size + nal_length_size) { + nalu->size = 0; + + return GST_H265_PARSER_NO_NAL_END; + } + + nalu->data = (guint8 *) data; + + if (!gst_h265_parse_nalu_header (nalu)) { + GST_WARNING ("error parsing \"NAL unit header\""); + nalu->size = 0; + return GST_H265_PARSER_BROKEN_DATA; + } + + if (nalu->size < 2) + return GST_H265_PARSER_BROKEN_DATA; + + nalu->valid = TRUE; + + return GST_H265_PARSER_OK; +} + +/** + * gst_h265_parser_identify_and_split_nalu_hevc: + * @parser: a #GstH265Parser + * @data: The data to parse, must be the beging of the Nal unit + * @offset: the offset from which to parse @data + * @size: the size of @data + * @nal_length_size: the size in bytes of the HEVC nal length prefix. + * @nalus: (element-type GstH265NalUnit): a caller allocated GArray of #GstH265NalUnit where to store parsed nal headers + * @consumed: the size of consumed bytes + * + * Parses @data for packetized (e.g., hvc1/hev1) bitstream and + * sets @nalus. In addition to nal identifying process, + * this method scans start-code prefix to split malformed packet into + * actual nal chunks. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.22 + */ +GstH265ParserResult +gst_h265_parser_identify_and_split_nalu_hevc (GstH265Parser * parser, + const guint8 * data, guint offset, gsize size, guint8 nal_length_size, + GArray * nalus, gsize * consumed) +{ + GstBitReader br; + guint nalu_size; + guint remaining; + guint off; + guint sc_size; + + g_return_val_if_fail (data != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (nalus != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5, + GST_H265_PARSER_ERROR); + + g_array_set_size (nalus, 0); + + if (consumed) + *consumed = 0; + + /* Would overflow guint below otherwise: the callers needs to ensure that + * this never happens */ + if (offset > G_MAXUINT32 - nal_length_size) { + GST_WARNING ("offset + nal_length_size overflow"); + return GST_H265_PARSER_BROKEN_DATA; + } + + if (size < offset + nal_length_size) { + GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT + ", offset %u", size, offset); + return GST_H265_PARSER_ERROR; + } + + /* Read nal unit size and unwrap the size field */ + gst_bit_reader_init (&br, data + offset, size - offset); + nalu_size = gst_bit_reader_get_bits_uint32_unchecked (&br, + nal_length_size * 8); + + if (nalu_size < 2) { + GST_WARNING ("too small nal size %d", nalu_size); + return GST_H265_PARSER_BROKEN_DATA; + } + + if (size < (gsize) nalu_size + nal_length_size) { + GST_WARNING ("larger nalu size %d than data size %" G_GSIZE_FORMAT, + nalu_size + nal_length_size, size); + return GST_H265_PARSER_BROKEN_DATA; + } + + if (consumed) + *consumed = nalu_size + nal_length_size; + + off = offset + nal_length_size; + remaining = nalu_size; + sc_size = nal_length_size; + + /* Drop trailing start-code since it will not be scanned */ + if (remaining >= 3) { + if (data[off + remaining - 1] == 0x01 && data[off + remaining - 2] == 0x00 + && data[off + remaining - 3] == 0x00) { + remaining -= 3; + + /* 4 bytes start-code */ + if (remaining > 0 && data[off + remaining - 1] == 0x00) + remaining--; + } + } + + /* Looping to split malformed nal units. nal-length field was dropped above + * so expected bitstream structure are: + * + * + * | nalu | + * sc scan result will be -1 and handled in CONDITION-A + * + * + * | SC | nalu | + * Hit CONDITION-C first then terminated in CONDITION-A + * + * + * | nalu | SC | nalu | ... + * CONDITION-B handles those cases + */ + do { + GstH265NalUnit nalu; + gint sc_offset = -1; + guint skip_size = 0; + + memset (&nalu, 0, sizeof (GstH265NalUnit)); + + /* startcode 3 bytes + minimum nal size 2 */ + if (remaining >= 5) + sc_offset = scan_for_start_codes (data + off, remaining); + + if (sc_offset < 0) { + if (remaining >= 2) { + /* CONDITION-A */ + /* Last chunk */ + nalu.size = remaining; + nalu.sc_offset = off - sc_size; + nalu.offset = off; + nalu.data = (guint8 *) data; + nalu.valid = TRUE; + + gst_h265_parse_nalu_header (&nalu); + g_array_append_val (nalus, nalu); + } + break; + } else if ((sc_offset == 2 && data[off + sc_offset - 1] != 0) + || sc_offset > 2) { + /* CONDITION-B */ + /* Found trailing startcode prefix */ + + nalu.size = sc_offset; + if (data[off + sc_offset - 1] == 0) { + /* 4 bytes start code */ + nalu.size--; + } + + nalu.sc_offset = off - sc_size; + nalu.offset = off; + nalu.data = (guint8 *) data; + nalu.valid = TRUE; + + gst_h265_parse_nalu_header (&nalu); + g_array_append_val (nalus, nalu); + } else { + /* CONDITION-C */ + /* startcode located at beginning of this chunk without actual nal data. + * skip this start code */ + } + + skip_size = sc_offset + 3; + if (skip_size >= remaining) + break; + + /* no more nal-length bytes but 3bytes startcode */ + sc_size = 3; + if (sc_offset > 0 && data[off + sc_offset - 1] == 0) + sc_size++; + + remaining -= skip_size; + off += skip_size; + } while (remaining >= 2); + + if (nalus->len > 0) + return GST_H265_PARSER_OK; + + GST_WARNING ("No nal found"); + + return GST_H265_PARSER_BROKEN_DATA; +} + +/** + * gst_h265_parser_parse_nal: + * @parser: a #GstH265Parser + * @nalu: The #GstH265NalUnit to parse + * + * This function should be called in the case one doesn't need to + * parse a specific structure. It is necessary to do so to make + * sure @parser is up to date. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_parse_nal (GstH265Parser * parser, GstH265NalUnit * nalu) +{ + GstH265VPS vps; + GstH265SPS sps; + GstH265PPS pps; + + switch (nalu->type) { + case GST_H265_NAL_VPS: + return gst_h265_parser_parse_vps (parser, nalu, &vps); + break; + case GST_H265_NAL_SPS: + return gst_h265_parser_parse_sps (parser, nalu, &sps, FALSE); + break; + case GST_H265_NAL_PPS: + return gst_h265_parser_parse_pps (parser, nalu, &pps); + } + + return GST_H265_PARSER_OK; +} + +/** + * gst_h265_parser_parse_vps: + * @parser: a #GstH265Parser + * @nalu: The %GST_H265_NAL_VPS #GstH265NalUnit to parse + * @vps: The #GstH265VPS to fill. + * + * Parses @data, and fills the @vps structure. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_parse_vps (GstH265Parser * parser, GstH265NalUnit * nalu, + GstH265VPS * vps) +{ + GstH265ParserResult res = gst_h265_parse_vps (nalu, vps); + + if (res == GST_H265_PARSER_OK) { + GST_DEBUG ("adding video parameter set with id: %d to array", vps->id); + + parser->vps[vps->id] = *vps; + parser->last_vps = &parser->vps[vps->id]; + } + + return res; +} + +/** + * gst_h265_parse_vps: + * @nalu: The %GST_H265_NAL_VPS #GstH265NalUnit to parse + * @vps: The #GstH265VPS to fill. + * + * Parses @data, and fills the @vps structure. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parse_vps (GstH265NalUnit * nalu, GstH265VPS * vps) +{ + NalReader nr; + guint i, j; + + GST_DEBUG ("parsing VPS"); + + nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes, + nalu->size - nalu->header_bytes); + + memset (vps, 0, sizeof (*vps)); + + vps->cprms_present_flag = 1; + + READ_UINT8 (&nr, vps->id, 4); + + READ_UINT8 (&nr, vps->base_layer_internal_flag, 1); + READ_UINT8 (&nr, vps->base_layer_available_flag, 1); + + READ_UINT8 (&nr, vps->max_layers_minus1, 6); + READ_UINT8 (&nr, vps->max_sub_layers_minus1, 3); + CHECK_ALLOWED (vps->max_sub_layers_minus1, 0, 6); + READ_UINT8 (&nr, vps->temporal_id_nesting_flag, 1); + + /* skip reserved_0xffff_16bits */ + if (!nal_reader_skip (&nr, 16)) + goto error; + + if (!gst_h265_parse_profile_tier_level (&vps->profile_tier_level, &nr, + vps->max_sub_layers_minus1)) + goto error; + + READ_UINT8 (&nr, vps->sub_layer_ordering_info_present_flag, 1); + + for (i = + (vps->sub_layer_ordering_info_present_flag ? 0 : + vps->max_sub_layers_minus1); i <= vps->max_sub_layers_minus1; i++) { + READ_UE_MAX (&nr, vps->max_dec_pic_buffering_minus1[i], MAX_DPB_SIZE - 1); + READ_UE_MAX (&nr, vps->max_num_reorder_pics[i], + vps->max_dec_pic_buffering_minus1[i]); + READ_UE_MAX (&nr, vps->max_latency_increase_plus1[i], G_MAXUINT32 - 1); + } + /* setting default values if vps->sub_layer_ordering_info_present_flag is zero */ + if (!vps->sub_layer_ordering_info_present_flag && vps->max_sub_layers_minus1) { + for (i = 0; i <= (vps->max_sub_layers_minus1 - 1); i++) { + vps->max_dec_pic_buffering_minus1[i] = + vps->max_dec_pic_buffering_minus1[vps->max_sub_layers_minus1]; + vps->max_num_reorder_pics[i] = + vps->max_num_reorder_pics[vps->max_sub_layers_minus1]; + vps->max_latency_increase_plus1[i] = + vps->max_latency_increase_plus1[vps->max_sub_layers_minus1]; + } + } + + READ_UINT8 (&nr, vps->max_layer_id, 6); + /* shall allow 63 */ + CHECK_ALLOWED_MAX (vps->max_layer_id, 63); + + READ_UE_MAX (&nr, vps->num_layer_sets_minus1, 1023); + /* allowed range is 0 to 1023 */ + CHECK_ALLOWED_MAX (vps->num_layer_sets_minus1, 1023); + + for (i = 1; i <= vps->num_layer_sets_minus1; i++) { + for (j = 0; j <= vps->max_layer_id; j++) { + /* layer_id_included_flag[i][j] */ + /* FIXME: need to parse this when we can support parsing multi-layer info. */ + if (!nal_reader_skip (&nr, 1)) + goto error; + } + } + + READ_UINT8 (&nr, vps->timing_info_present_flag, 1); + + if (vps->timing_info_present_flag) { + READ_UINT32 (&nr, vps->num_units_in_tick, 32); + READ_UINT32 (&nr, vps->time_scale, 32); + READ_UINT8 (&nr, vps->poc_proportional_to_timing_flag, 1); + + if (vps->poc_proportional_to_timing_flag) + READ_UE_MAX (&nr, vps->num_ticks_poc_diff_one_minus1, G_MAXUINT32 - 1); + + READ_UE_MAX (&nr, vps->num_hrd_parameters, 1024); + /* allowed range is + * 0 to vps_num_layer_sets_minus1 + 1 */ + CHECK_ALLOWED_MAX (vps->num_hrd_parameters, vps->num_layer_sets_minus1 + 1); + + if (vps->num_hrd_parameters) { + READ_UE_MAX (&nr, vps->hrd_layer_set_idx, 1023); + /* allowed range is + * ( vps_base_layer_internal_flag ? 0 : 1 ) to vps_num_layer_sets_minus1 + */ + CHECK_ALLOWED_MAX (vps->hrd_layer_set_idx, vps->num_layer_sets_minus1); + + if (!gst_h265_parse_hrd_parameters (&vps->hrd_params, &nr, + vps->cprms_present_flag, vps->max_sub_layers_minus1)) + goto error; + } + + /* FIXME: VPS can have multiple hrd parameters, and therefore hrd_params + * should be an array (like Garray). But it also requires new _clear() + * method for free the array in GstH265VPS whenever gst_h265_parse_vps() + * is called. Need to work for multi-layer related parsing supporting + * + * FIXME: Following code is just work around to find correct + * vps_extension position */ + + /* skip the first parsed one above */ + for (i = 1; i < vps->num_hrd_parameters; i++) { + guint16 hrd_layer_set_idx; + guint8 cprms_present_flag; + GstH265HRDParams hrd_params; + + READ_UE_MAX (&nr, hrd_layer_set_idx, 1023); + CHECK_ALLOWED_MAX (hrd_layer_set_idx, vps->num_layer_sets_minus1); + + /* need parsing if (i > 1) */ + READ_UINT8 (&nr, cprms_present_flag, 1); + + if (!gst_h265_parse_hrd_parameters (&hrd_params, &nr, + cprms_present_flag, vps->max_sub_layers_minus1)) + goto error; + } + } + READ_UINT8 (&nr, vps->vps_extension, 1); + vps->valid = TRUE; + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Video parameter set\""); + vps->valid = FALSE; + return GST_H265_PARSER_ERROR; +} + +/** + * gst_h265_parser_parse_sps_ext: + * @parser: a #GstH265Parser + * @nalu: The %GST_H265_NAL_SPS #GstH265NalUnit to parse + * @sps: The #GstH265SPS to fill. + * @sps_ext: The #GstH265SPSEXT matching the #GstH265SPS to fill. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses @data, and fills the @sps structure. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.28 + */ +GstH265ParserResult +gst_h265_parser_parse_sps_ext (GstH265Parser * parser, GstH265NalUnit * nalu, + GstH265SPS * sps, GstH265SPSEXT * sps_ext, gboolean parse_vui_params) +{ + GstH265ParserResult res = + gst_h265_parse_sps_ext (parser, nalu, sps, sps_ext, parse_vui_params); + + if (res == GST_H265_PARSER_OK) { + GST_DEBUG ("adding sequence parameter set with id: %d to array", sps->id); + + parser->sps[sps->id] = *sps; + parser->last_sps = &parser->sps[sps->id]; + } + + return res; +} + +/** + * gst_h265_parser_parse_sps: + * @parser: a #GstH265Parser + * @nalu: The %GST_H265_NAL_SPS #GstH265NalUnit to parse + * @sps: The #GstH265SPS to fill. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses @data, and fills the @sps structure. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_parse_sps (GstH265Parser * parser, GstH265NalUnit * nalu, + GstH265SPS * sps, gboolean parse_vui_params) +{ + GstH265SPSEXT sps_ext; + + return gst_h265_parser_parse_sps_ext (parser, nalu, sps, &sps_ext, + parse_vui_params); +} + +/** + * gst_h265_parse_sps_ext: + * parser: The #GstH265Parser + * @nalu: The %GST_H265_NAL_SPS #GstH265NalUnit to parse + * @sps: The #GstH265SPS to fill. + * @sps_ext: The #GstH265SPSEXT matching the #GstH265SPS to fill. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses @data, and fills the @sps structure. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.28 + */ +GstH265ParserResult +gst_h265_parse_sps_ext (GstH265Parser * parser, GstH265NalUnit * nalu, + GstH265SPS * sps, GstH265SPSEXT * sps_ext, gboolean parse_vui_params) +{ + NalReader nr; + guint i; + guint subwc[] = { 1, 2, 2, 1, 1 }; + guint subhc[] = { 1, 2, 1, 1, 1 }; + + GST_DEBUG ("parsing SPS"); + + nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes, + nalu->size - nalu->header_bytes); + + memset (sps, 0, sizeof (*sps)); + + READ_UINT8 (&nr, sps->vps_id, 4); + + READ_UINT8 (&nr, sps->max_sub_layers_minus1, 3); + CHECK_ALLOWED (sps->max_sub_layers_minus1, 0, 6); + READ_UINT8 (&nr, sps->temporal_id_nesting_flag, 1); + + if (!gst_h265_parse_profile_tier_level (&sps->profile_tier_level, &nr, + sps->max_sub_layers_minus1)) + goto error; + + READ_UE_MAX (&nr, sps->id, GST_H265_MAX_SPS_COUNT - 1); + + READ_UE_MAX (&nr, sps->chroma_format_idc, 3); + if (sps->chroma_format_idc == 3) + READ_UINT8 (&nr, sps->separate_colour_plane_flag, 1); + + READ_UE_ALLOWED (&nr, sps->pic_width_in_luma_samples, 1, 16888); + READ_UE_ALLOWED (&nr, sps->pic_height_in_luma_samples, 1, 16888); + + READ_UINT8 (&nr, sps->conformance_window_flag, 1); + if (sps->conformance_window_flag) { + READ_UE (&nr, sps->conf_win_left_offset); + READ_UE (&nr, sps->conf_win_right_offset); + READ_UE (&nr, sps->conf_win_top_offset); + READ_UE (&nr, sps->conf_win_bottom_offset); + } + + READ_UE_MAX (&nr, sps->bit_depth_luma_minus8, 6); + READ_UE_MAX (&nr, sps->bit_depth_chroma_minus8, 6); + READ_UE_MAX (&nr, sps->log2_max_pic_order_cnt_lsb_minus4, 12); + + READ_UINT8 (&nr, sps->sub_layer_ordering_info_present_flag, 1); + for (i = + (sps->sub_layer_ordering_info_present_flag ? 0 : + sps->max_sub_layers_minus1); i <= sps->max_sub_layers_minus1; i++) { + READ_UE_MAX (&nr, sps->max_dec_pic_buffering_minus1[i], MAX_DPB_SIZE - 1); + READ_UE_MAX (&nr, sps->max_num_reorder_pics[i], + sps->max_dec_pic_buffering_minus1[i]); + READ_UE_MAX (&nr, sps->max_latency_increase_plus1[i], G_MAXUINT32 - 1); + } + /* setting default values if sps->sub_layer_ordering_info_present_flag is zero */ + if (!sps->sub_layer_ordering_info_present_flag && sps->max_sub_layers_minus1) { + for (i = 0; i <= (sps->max_sub_layers_minus1 - 1); i++) { + sps->max_dec_pic_buffering_minus1[i] = + sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1]; + sps->max_num_reorder_pics[i] = + sps->max_num_reorder_pics[sps->max_sub_layers_minus1]; + sps->max_latency_increase_plus1[i] = + sps->max_latency_increase_plus1[sps->max_sub_layers_minus1]; + } + } + + /* The limits are calculted based on the profile_tier_level constraint + * in Annex-A: CtbLog2SizeY = 4 to 6 */ + READ_UE_MAX (&nr, sps->log2_min_luma_coding_block_size_minus3, 3); + READ_UE_MAX (&nr, sps->log2_diff_max_min_luma_coding_block_size, 6); + READ_UE_MAX (&nr, sps->log2_min_transform_block_size_minus2, 3); + READ_UE_MAX (&nr, sps->log2_diff_max_min_transform_block_size, 3); + READ_UE_MAX (&nr, sps->max_transform_hierarchy_depth_inter, 4); + READ_UE_MAX (&nr, sps->max_transform_hierarchy_depth_intra, 4); + + READ_UINT8 (&nr, sps->scaling_list_enabled_flag, 1); + if (sps->scaling_list_enabled_flag) { + READ_UINT8 (&nr, sps->scaling_list_data_present_flag, 1); + + if (sps->scaling_list_data_present_flag) + if (!gst_h265_parser_parse_scaling_lists (&nr, &sps->scaling_list, FALSE)) + goto error; + } + + READ_UINT8 (&nr, sps->amp_enabled_flag, 1); + READ_UINT8 (&nr, sps->sample_adaptive_offset_enabled_flag, 1); + READ_UINT8 (&nr, sps->pcm_enabled_flag, 1); + + if (sps->pcm_enabled_flag) { + READ_UINT8 (&nr, sps->pcm_sample_bit_depth_luma_minus1, 4); + READ_UINT8 (&nr, sps->pcm_sample_bit_depth_chroma_minus1, 4); + READ_UE_MAX (&nr, sps->log2_min_pcm_luma_coding_block_size_minus3, 2); + READ_UE_MAX (&nr, sps->log2_diff_max_min_pcm_luma_coding_block_size, 2); + READ_UINT8 (&nr, sps->pcm_loop_filter_disabled_flag, 1); + } + + READ_UE_MAX (&nr, sps->num_short_term_ref_pic_sets, 64); + for (i = 0; i < sps->num_short_term_ref_pic_sets; i++) + if (!gst_h265_parser_parse_short_term_ref_pic_sets + (&sps->short_term_ref_pic_set[i], &nr, i, sps, sps_ext)) + goto error; + + READ_UINT8 (&nr, sps->long_term_ref_pics_present_flag, 1); + if (sps->long_term_ref_pics_present_flag) { + READ_UE_MAX (&nr, sps->num_long_term_ref_pics_sps, 32); + for (i = 0; i < sps->num_long_term_ref_pics_sps; i++) { + READ_UINT16 (&nr, sps->lt_ref_pic_poc_lsb_sps[i], + sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + READ_UINT8 (&nr, sps->used_by_curr_pic_lt_sps_flag[i], 1); + } + } + + READ_UINT8 (&nr, sps->temporal_mvp_enabled_flag, 1); + READ_UINT8 (&nr, sps->strong_intra_smoothing_enabled_flag, 1); + READ_UINT8 (&nr, sps->vui_parameters_present_flag, 1); + + if (sps->vui_parameters_present_flag && parse_vui_params) + if (!gst_h265_parse_vui_parameters (sps, &nr)) + goto error; + + READ_UINT8 (&nr, sps->sps_extension_flag, 1); + + if (sps->sps_extension_flag) { + READ_UINT8 (&nr, sps->sps_range_extension_flag, 1); + READ_UINT8 (&nr, sps->sps_multilayer_extension_flag, 1); + READ_UINT8 (&nr, sps->sps_3d_extension_flag, 1); + READ_UINT8 (&nr, sps->sps_scc_extension_flag, 1); + READ_UINT8 (&nr, sps->sps_extension_4bits, 4); + } + + if (sps->sps_range_extension_flag) { + READ_UINT8 (&nr, + sps->sps_extension_params.transform_skip_rotation_enabled_flag, 1); + READ_UINT8 (&nr, + sps->sps_extension_params.transform_skip_context_enabled_flag, 1); + READ_UINT8 (&nr, sps->sps_extension_params.implicit_rdpcm_enabled_flag, 1); + READ_UINT8 (&nr, sps->sps_extension_params.explicit_rdpcm_enabled_flag, 1); + READ_UINT8 (&nr, + sps->sps_extension_params.extended_precision_processing_flag, 1); + READ_UINT8 (&nr, sps->sps_extension_params.intra_smoothing_disabled_flag, + 1); + READ_UINT8 (&nr, + sps->sps_extension_params.high_precision_offsets_enabled_flag, 1); + READ_UINT8 (&nr, + sps->sps_extension_params.persistent_rice_adaptation_enabled_flag, 1); + READ_UINT8 (&nr, + sps->sps_extension_params.cabac_bypass_alignment_enabled_flag, 1); + } + + if (sps->sps_multilayer_extension_flag) { + GST_WARNING ("do not support multilayer extension, skip all" + " remaining bits"); + goto done; + } + if (sps->sps_3d_extension_flag) { + GST_WARNING ("do not support 3d extension, skip all remaining bits"); + goto done; + } + + if (sps->sps_scc_extension_flag) { + READ_UINT8 (&nr, + sps->sps_scc_extension_params.sps_curr_pic_ref_enabled_flag, 1); + READ_UINT8 (&nr, sps->sps_scc_extension_params.palette_mode_enabled_flag, + 1); + if (sps->sps_scc_extension_params.palette_mode_enabled_flag) { + READ_UE_MAX (&nr, sps->sps_scc_extension_params.palette_max_size, 64); + READ_UE_MAX (&nr, + sps->sps_scc_extension_params.delta_palette_max_predictor_size, + 128 - sps->sps_scc_extension_params.palette_max_size); + + READ_UINT8 (&nr, + sps-> + sps_scc_extension_params.sps_palette_predictor_initializers_present_flag, + 1); + if (sps-> + sps_scc_extension_params.sps_palette_predictor_initializers_present_flag) + { + guint comp; + READ_UE_MAX (&nr, + sps-> + sps_scc_extension_params.sps_num_palette_predictor_initializer_minus1, + sps->sps_scc_extension_params.palette_max_size + + sps->sps_scc_extension_params.delta_palette_max_predictor_size - 1); + + for (comp = 0; comp < (sps->chroma_format_idc == 0 ? 1 : 3); comp++) { + guint num_bits; + guint num = + sps-> + sps_scc_extension_params.sps_num_palette_predictor_initializer_minus1 + + 1; + + num_bits = (comp == 0 ? sps->bit_depth_luma_minus8 + 8 : + sps->bit_depth_chroma_minus8 + 8); + for (i = 0; i < num; i++) + READ_UINT32 (&nr, + sps->sps_scc_extension_params.sps_palette_predictor_initializer + [comp] + [i], num_bits); + } + } + } + + READ_UINT8 (&nr, + sps->sps_scc_extension_params.motion_vector_resolution_control_idc, 2); + READ_UINT8 (&nr, + sps->sps_scc_extension_params.intra_boundary_filtering_disabled_flag, + 1); + } + +done: + /* calculate ChromaArrayType */ + if (!sps->separate_colour_plane_flag) + sps->chroma_array_type = sps->chroma_format_idc; + + /* Calculate width and height */ + sps->width = sps->pic_width_in_luma_samples; + sps->height = sps->pic_height_in_luma_samples; + if (sps->width < 0 || sps->height < 0) { + GST_WARNING ("invalid width/height in SPS"); + goto error; + } + + if (sps->conformance_window_flag) { + const guint crop_unit_x = subwc[sps->chroma_format_idc]; + const guint crop_unit_y = subhc[sps->chroma_format_idc]; + + sps->crop_rect_width = sps->width - + (sps->conf_win_left_offset + sps->conf_win_right_offset) * crop_unit_x; + sps->crop_rect_height = sps->height - + (sps->conf_win_top_offset + sps->conf_win_bottom_offset) * crop_unit_y; + sps->crop_rect_x = sps->conf_win_left_offset * crop_unit_x; + sps->crop_rect_y = sps->conf_win_top_offset * crop_unit_y; + + GST_LOG ("crop_rectangle x=%u y=%u width=%u, height=%u", sps->crop_rect_x, + sps->crop_rect_y, sps->crop_rect_width, sps->crop_rect_height); + } + + sps->fps_num = 0; + sps->fps_den = 1; + + sps->valid = TRUE; + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Sequence parameter set\""); + sps->valid = FALSE; + return GST_H265_PARSER_ERROR; +} + +/** + * gst_h265_parse_sps: + * parser: The #GstH265Parser + * @nalu: The %GST_H265_NAL_SPS #GstH265NalUnit to parse + * @sps: The #GstH265SPS to fill. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses @data, and fills the @sps structure. + * + * Returns: a #GstH265ParserResult + */ + +GstH265ParserResult +gst_h265_parse_sps (GstH265Parser * parser, GstH265NalUnit * nalu, + GstH265SPS * sps, gboolean parse_vui_params) +{ + GstH265SPSEXT sps_ext = { 0 }; + return gst_h265_parse_sps_ext (parser, nalu, sps, &sps_ext, parse_vui_params); +} + +/** + * gst_h265_parse_pps: + * @parser: a #GstH265Parser + * @nalu: The %GST_H265_NAL_PPS #GstH265NalUnit to parse + * @pps: The #GstH265PPS to fill. + * + * Parses @data, and fills the @pps structure. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parse_pps (GstH265Parser * parser, GstH265NalUnit * nalu, + GstH265PPS * pps) +{ + guint32 MaxBitDepthY, MaxBitDepthC; + NalReader nr; + guint8 i; + + GST_DEBUG ("parsing PPS"); + + nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes, + nalu->size - nalu->header_bytes); + + memset (pps, 0, sizeof (*pps)); + + READ_UE_MAX (&nr, pps->id, GST_H265_MAX_PPS_COUNT - 1); + READ_UE_MAX (&nr, pps->sps_id, GST_H265_MAX_SPS_COUNT - 1); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + pps->uniform_spacing_flag = 1; + pps->loop_filter_across_tiles_enabled_flag = 1; + + READ_UINT8 (&nr, pps->dependent_slice_segments_enabled_flag, 1); + READ_UINT8 (&nr, pps->output_flag_present_flag, 1); + READ_UINT8 (&nr, pps->num_extra_slice_header_bits, 3); + READ_UINT8 (&nr, pps->sign_data_hiding_enabled_flag, 1); + READ_UINT8 (&nr, pps->cabac_init_present_flag, 1); + + READ_UE_MAX (&nr, pps->num_ref_idx_l0_default_active_minus1, 14); + READ_UE_MAX (&nr, pps->num_ref_idx_l1_default_active_minus1, 14); + + /* The value of init_qp_minus26 shall be in the range of + * −( 26 + QpBdOffsetY ) to +25, inclusive. + * QpBdOffsetY = 6 * bit_depth_luma_minus8 (7-5) + * and bit_depth_luma_minus8 shall be in the range of 0 to 8, inclusive. + * so the minimum possible value of init_qp_minus26 is -(26 + 6*8) */ + READ_SE_ALLOWED (&nr, pps->init_qp_minus26, -(26 + 6 * 8), 25); + + READ_UINT8 (&nr, pps->constrained_intra_pred_flag, 1); + READ_UINT8 (&nr, pps->transform_skip_enabled_flag, 1); + + READ_UINT8 (&nr, pps->cu_qp_delta_enabled_flag, 1); + if (pps->cu_qp_delta_enabled_flag) { + READ_UE_MAX (&nr, pps->diff_cu_qp_delta_depth, 6); + } + + READ_SE_ALLOWED (&nr, pps->cb_qp_offset, -12, 12); + READ_SE_ALLOWED (&nr, pps->cr_qp_offset, -12, 12); + + READ_UINT8 (&nr, pps->slice_chroma_qp_offsets_present_flag, 1); + READ_UINT8 (&nr, pps->weighted_pred_flag, 1); + READ_UINT8 (&nr, pps->weighted_bipred_flag, 1); + READ_UINT8 (&nr, pps->transquant_bypass_enabled_flag, 1); + READ_UINT8 (&nr, pps->tiles_enabled_flag, 1); + READ_UINT8 (&nr, pps->entropy_coding_sync_enabled_flag, 1); + + if (pps->tiles_enabled_flag) { + GstH265SPS *sps; + guint32 CtbSizeY, MinCbLog2SizeY, CtbLog2SizeY; + + sps = gst_h265_parser_get_sps (parser, pps->sps_id); + if (!sps) { + GST_WARNING + ("couldn't find associated sequence parameter set with id: %d", + pps->sps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + + MinCbLog2SizeY = sps->log2_min_luma_coding_block_size_minus3 + 3; + CtbLog2SizeY = + MinCbLog2SizeY + sps->log2_diff_max_min_luma_coding_block_size; + CtbSizeY = 1 << CtbLog2SizeY; + pps->PicHeightInCtbsY = + div_ceil (sps->pic_height_in_luma_samples, CtbSizeY); + pps->PicWidthInCtbsY = div_ceil (sps->pic_width_in_luma_samples, CtbSizeY); + + READ_UE_MAX (&nr, pps->num_tile_columns_minus1, pps->PicWidthInCtbsY - 1); + READ_UE_MAX (&nr, pps->num_tile_rows_minus1, pps->PicHeightInCtbsY - 1); + + if (pps->num_tile_columns_minus1 + 1 > + G_N_ELEMENTS (pps->column_width_minus1)) { + GST_WARNING ("Invalid \"num_tile_columns_minus1\" %d", + pps->num_tile_columns_minus1); + goto error; + } + + if (pps->num_tile_rows_minus1 + 1 > G_N_ELEMENTS (pps->row_height_minus1)) { + GST_WARNING ("Invalid \"num_tile_rows_minus1\" %d", + pps->num_tile_rows_minus1); + goto error; + } + + READ_UINT8 (&nr, pps->uniform_spacing_flag, 1); + /* 6.5.1, 6-4, 6-5, 7.4.3.3.1 */ + if (pps->uniform_spacing_flag) { + guint8 num_col = pps->num_tile_columns_minus1 + 1; + guint8 num_row = pps->num_tile_rows_minus1 + 1; + for (i = 0; i < num_col; i++) { + pps->column_width_minus1[i] = + ((i + 1) * pps->PicWidthInCtbsY / num_col + - i * pps->PicWidthInCtbsY / num_col) - 1; + } + for (i = 0; i < num_row; i++) { + pps->row_height_minus1[i] = + ((i + 1) * pps->PicHeightInCtbsY / num_row + - i * pps->PicHeightInCtbsY / num_row) - 1; + } + } else { + pps->column_width_minus1[pps->num_tile_columns_minus1] = + pps->PicWidthInCtbsY - 1; + for (i = 0; i < pps->num_tile_columns_minus1; i++) { + READ_UE (&nr, pps->column_width_minus1[i]); + pps->column_width_minus1[pps->num_tile_columns_minus1] -= + (pps->column_width_minus1[i] + 1); + } + + pps->row_height_minus1[pps->num_tile_rows_minus1] = + pps->PicHeightInCtbsY - 1; + for (i = 0; i < pps->num_tile_rows_minus1; i++) { + READ_UE (&nr, pps->row_height_minus1[i]); + pps->row_height_minus1[pps->num_tile_rows_minus1] -= + (pps->row_height_minus1[i] + 1); + } + } + READ_UINT8 (&nr, pps->loop_filter_across_tiles_enabled_flag, 1); + } + + READ_UINT8 (&nr, pps->loop_filter_across_slices_enabled_flag, 1); + + READ_UINT8 (&nr, pps->deblocking_filter_control_present_flag, 1); + if (pps->deblocking_filter_control_present_flag) { + READ_UINT8 (&nr, pps->deblocking_filter_override_enabled_flag, 1); + + READ_UINT8 (&nr, pps->deblocking_filter_disabled_flag, 1); + if (!pps->deblocking_filter_disabled_flag) { + READ_SE_ALLOWED (&nr, pps->beta_offset_div2, -6, 6); + READ_SE_ALLOWED (&nr, pps->tc_offset_div2, -6, +6); + } + } + + READ_UINT8 (&nr, pps->scaling_list_data_present_flag, 1); + if (pps->scaling_list_data_present_flag) + if (!gst_h265_parser_parse_scaling_lists (&nr, &pps->scaling_list, FALSE)) + goto error; + + READ_UINT8 (&nr, pps->lists_modification_present_flag, 1); + READ_UE_MAX (&nr, pps->log2_parallel_merge_level_minus2, 4); + READ_UINT8 (&nr, pps->slice_segment_header_extension_present_flag, 1); + READ_UINT8 (&nr, pps->pps_extension_flag, 1); + + if (pps->pps_extension_flag) { + READ_UINT8 (&nr, pps->pps_range_extension_flag, 1); + READ_UINT8 (&nr, pps->pps_multilayer_extension_flag, 1); + READ_UINT8 (&nr, pps->pps_3d_extension_flag, 1); + READ_UINT8 (&nr, pps->pps_scc_extension_flag, 1); + READ_UINT8 (&nr, pps->pps_extension_4bits, 4); + } + + if (pps->pps_range_extension_flag) { + GstH265SPS *sps; + + sps = gst_h265_parser_get_sps (parser, pps->sps_id); + if (!sps) { + GST_WARNING + ("couldn't find associated sequence parameter set with id: %d", + pps->sps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + + if (pps->transform_skip_enabled_flag) + READ_UE (&nr, + pps->pps_extension_params.log2_max_transform_skip_block_size_minus2); + READ_UINT8 (&nr, + pps->pps_extension_params.cross_component_prediction_enabled_flag, 1); + READ_UINT8 (&nr, + pps->pps_extension_params.chroma_qp_offset_list_enabled_flag, 1); + if (pps->pps_extension_params.chroma_qp_offset_list_enabled_flag) { + READ_UE_MAX (&nr, + pps->pps_extension_params.diff_cu_chroma_qp_offset_depth, + sps->log2_diff_max_min_luma_coding_block_size); + READ_UE_MAX (&nr, + pps->pps_extension_params.chroma_qp_offset_list_len_minus1, 5); + for (i = 0; + i <= pps->pps_extension_params.chroma_qp_offset_list_len_minus1; + i++) { + READ_SE_ALLOWED (&nr, pps->pps_extension_params.cb_qp_offset_list[i], + -12, 12); + READ_SE_ALLOWED (&nr, pps->pps_extension_params.cr_qp_offset_list[i], + -12, 12); + } + } + MaxBitDepthY = + sps->bit_depth_luma_minus8 > 2 ? sps->bit_depth_luma_minus8 - 2 : 0; + MaxBitDepthC = + sps->bit_depth_chroma_minus8 > 2 ? sps->bit_depth_chroma_minus8 - 2 : 0; + READ_UE_MAX (&nr, pps->pps_extension_params.log2_sao_offset_scale_luma, + MaxBitDepthY); + READ_UE_MAX (&nr, + pps->pps_extension_params.log2_sao_offset_scale_chroma, MaxBitDepthC); + } + + if (pps->pps_multilayer_extension_flag) { + GST_WARNING ("do not support multilayer extension, skip all" + " remaining bits"); + goto done; + } + if (pps->pps_3d_extension_flag) { + GST_WARNING ("do not support 3d extension, skip all remaining bits"); + goto done; + } + + if (pps->pps_scc_extension_flag) { + GstH265SPS *sps; + + sps = gst_h265_parser_get_sps (parser, pps->sps_id); + if (!sps) { + GST_WARNING + ("couldn't find associated sequence parameter set with id: %d", + pps->sps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + + READ_UINT8 (&nr, + pps->pps_scc_extension_params.pps_curr_pic_ref_enabled_flag, 1); + READ_UINT8 (&nr, + pps-> + pps_scc_extension_params.residual_adaptive_colour_transform_enabled_flag, + 1); + if (pps-> + pps_scc_extension_params.residual_adaptive_colour_transform_enabled_flag) + { + READ_UINT8 (&nr, + pps->pps_scc_extension_params.pps_slice_act_qp_offsets_present_flag, + 1); + READ_SE_ALLOWED (&nr, + pps->pps_scc_extension_params.pps_act_y_qp_offset_plus5, -7, 17); + READ_SE_ALLOWED (&nr, + pps->pps_scc_extension_params.pps_act_cb_qp_offset_plus5, -7, 17); + READ_SE_ALLOWED (&nr, + pps->pps_scc_extension_params.pps_act_cr_qp_offset_plus3, -9, 15); + } + + READ_UINT8 (&nr, + pps-> + pps_scc_extension_params.pps_palette_predictor_initializers_present_flag, + 1); + if (pps-> + pps_scc_extension_params.pps_palette_predictor_initializers_present_flag) + { + READ_UE_MAX (&nr, + pps->pps_scc_extension_params.pps_num_palette_predictor_initializer, + sps->sps_scc_extension_params.palette_max_size + + sps->sps_scc_extension_params.delta_palette_max_predictor_size); + if (pps->pps_scc_extension_params.pps_num_palette_predictor_initializer > + 0) { + guint comp; + + READ_UINT8 (&nr, pps->pps_scc_extension_params.monochrome_palette_flag, + 1); + /* It is a requirement of bitstream conformance that the value of + luma_bit_depth_entry_minus8 shall be equal to the value of + bit_depth_luma_minus8 */ + READ_UE_ALLOWED (&nr, + pps->pps_scc_extension_params.luma_bit_depth_entry_minus8, + sps->bit_depth_luma_minus8, sps->bit_depth_luma_minus8); + if (!pps->pps_scc_extension_params.monochrome_palette_flag) { + /* It is a requirement of bitstream conformance that the value + of chroma_bit_depth_entry_minus8 shall be equal to the value + of bit_depth_chroma_minus8. */ + READ_UE_ALLOWED (&nr, + pps->pps_scc_extension_params.chroma_bit_depth_entry_minus8, + sps->bit_depth_chroma_minus8, sps->bit_depth_chroma_minus8); + } + + for (comp = 0; comp < + (pps->pps_scc_extension_params.monochrome_palette_flag ? 1 : 3); + comp++) { + guint num_bits; + guint num = + pps-> + pps_scc_extension_params.pps_num_palette_predictor_initializer; + + num_bits = (comp == 0 ? + pps->pps_scc_extension_params.luma_bit_depth_entry_minus8 + 8 : + pps->pps_scc_extension_params.chroma_bit_depth_entry_minus8 + 8); + for (i = 0; i < num; i++) + READ_UINT32 (&nr, + pps->pps_scc_extension_params.pps_palette_predictor_initializer + [comp][i], num_bits); + } + } + } + } + +done: + pps->valid = TRUE; + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Picture parameter set\""); + pps->valid = FALSE; + return GST_H265_PARSER_ERROR; +} + +/** + * gst_h265_parser_parse_pps: + * @parser: a #GstH265Parser + * @nalu: The %GST_H265_NAL_PPS #GstH265NalUnit to parse + * @pps: The #GstH265PPS to fill. + * + * Parses @data, and fills the @pps structure. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_parse_pps (GstH265Parser * parser, + GstH265NalUnit * nalu, GstH265PPS * pps) +{ + GstH265ParserResult res = gst_h265_parse_pps (parser, nalu, pps); + if (res == GST_H265_PARSER_OK) { + GST_DEBUG ("adding picture parameter set with id: %d to array", pps->id); + + parser->pps[pps->id] = *pps; + parser->last_pps = &parser->pps[pps->id]; + } + + return res; +} + +static GstH265ParserResult +gst_h265_parser_fill_sps (GstH265Parser * parser, GstH265SPS * sps) +{ + GstH265VPS *vps; + GstH265VUIParams *vui = &sps->vui_params; + GstH265ParserResult ret = GST_H265_PARSER_OK; + + vps = gst_h265_parser_get_vps (parser, sps->vps_id); + if (!vps) { + GST_DEBUG ("couldn't find associated video parameter set with id: %d", + sps->vps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + sps->vps = vps; + + if (vui && vui->timing_info_present_flag) { + /* derive framerate for progressive stream if the pic_struct + * syntax element is not present in picture timing SEI messages */ + /* Fixme: handle other cases also */ + if (vui->parsed && vui->timing_info_present_flag + && !vui->field_seq_flag && !vui->frame_field_info_present_flag) { + sps->fps_num = vui->time_scale; + sps->fps_den = vui->num_units_in_tick; + GST_LOG ("framerate %d/%d in VUI", sps->fps_num, sps->fps_den); + } + } else if (vps && vps->timing_info_present_flag) { + sps->fps_num = vps->time_scale; + sps->fps_den = vps->num_units_in_tick; + GST_LOG ("framerate %d/%d in VPS", sps->fps_num, sps->fps_den); + } else { + GST_LOG ("No VUI, unknown framerate"); + } + + return ret; +} + +static GstH265ParserResult +gst_h265_parser_fill_pps (GstH265Parser * parser, GstH265PPS * pps) +{ + GstH265SPS *sps; + gint qp_bd_offset; + guint32 CtbSizeY, MinCbLog2SizeY, CtbLog2SizeY; + GstH265ParserResult ret = GST_H265_PARSER_OK; + + sps = gst_h265_parser_get_sps (parser, pps->sps_id); + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + pps->sps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + + ret = gst_h265_parser_fill_sps (parser, sps); + if (ret != GST_H265_PARSER_OK) { + GST_WARNING ("couldn't fill sps id: %d", pps->sps_id); + return ret; + } + + pps->sps = sps; + qp_bd_offset = 6 * sps->bit_depth_luma_minus8; + + MinCbLog2SizeY = sps->log2_min_luma_coding_block_size_minus3 + 3; + CtbLog2SizeY = MinCbLog2SizeY + sps->log2_diff_max_min_luma_coding_block_size; + CtbSizeY = 1 << CtbLog2SizeY; + pps->PicHeightInCtbsY = div_ceil (sps->pic_height_in_luma_samples, CtbSizeY); + pps->PicWidthInCtbsY = div_ceil (sps->pic_width_in_luma_samples, CtbSizeY); + + if (pps->init_qp_minus26 < -(26 + qp_bd_offset)) + return GST_H265_PARSER_BROKEN_LINK; + + if (sps->scaling_list_enabled_flag && !sps->scaling_list_data_present_flag + && !pps->scaling_list_data_present_flag) + if (!gst_h265_parser_parse_scaling_lists (NULL, &pps->scaling_list, TRUE)) + return GST_H265_PARSER_BROKEN_LINK; + + if (pps->cu_qp_delta_enabled_flag) + if (pps->diff_cu_qp_delta_depth > + sps->log2_diff_max_min_luma_coding_block_size) + return GST_H265_PARSER_BROKEN_LINK; + + return ret; +} + +/** + * gst_h265_parser_parse_slice_hdr_ext: + * @parser: a #GstH265Parser + * @nalu: The `GST_H265_NAL_SLICE` #GstH265NalUnit to parse + * @slice: The #GstH265SliceHdr to fill. + * @sps_ext: The #GstH265SPSEXT to fill. + * + * Parses @data, and fills the @slice structure. + * The resulting @slice_hdr structure shall be deallocated with + * gst_h265_slice_hdr_free() when it is no longer needed + * + * Returns: a #GstH265ParserResult + * + * Since: 1.28 + */ +GstH265ParserResult +gst_h265_parser_parse_slice_hdr_ext (GstH265Parser * parser, + GstH265NalUnit * nalu, GstH265SliceHdr * slice, GstH265SPSEXT * sps_ext) +{ + NalReader nr; + gint pps_id; + GstH265PPS *pps; + GstH265SPS *sps; + guint i; + GstH265ShortTermRefPicSet *stRPS = NULL; + guint32 UsedByCurrPicLt[16]; + guint32 PicSizeInCtbsY; + gint NumPocTotalCurr = 0; + GstH265ParserResult err; + + memset (slice, 0, sizeof (*slice)); + + if (!nalu->size) { + GST_DEBUG ("Invalid Nal Unit"); + return GST_H265_PARSER_ERROR; + } + + nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes, + nalu->size - nalu->header_bytes); + + GST_DEBUG ("parsing \"Slice header\", slice type"); + + READ_UINT8 (&nr, slice->first_slice_segment_in_pic_flag, 1); + + if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type)) + READ_UINT8 (&nr, slice->no_output_of_prior_pics_flag, 1); + + READ_UE_MAX (&nr, pps_id, GST_H265_MAX_PPS_COUNT - 1); + pps = gst_h265_parser_get_pps (parser, pps_id); + if (!pps) { + GST_WARNING + ("couldn't find associated picture parameter set with id: %d", pps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + + err = gst_h265_parser_fill_pps (parser, pps); + if (err != GST_H265_PARSER_OK) { + GST_WARNING ("couldn't fill pps id: %d", pps_id); + return err; + } + + slice->pps_id = pps_id; + slice->pps = pps; + sps = pps->sps; + if (!sps) { + GST_WARNING + ("couldn't find associated sequence parameter set with id: %d", + pps->id); + return GST_H265_PARSER_BROKEN_LINK; + } + + PicSizeInCtbsY = pps->PicWidthInCtbsY * pps->PicHeightInCtbsY; + /* set default values for fields that might not be present in the bitstream + * and have valid defaults */ + slice->pic_output_flag = 1; + slice->collocated_from_l0_flag = 1; + slice->deblocking_filter_disabled_flag = pps->deblocking_filter_disabled_flag; + slice->beta_offset_div2 = pps->beta_offset_div2; + slice->tc_offset_div2 = pps->tc_offset_div2; + slice->loop_filter_across_slices_enabled_flag = + pps->loop_filter_across_slices_enabled_flag; + + if (!slice->first_slice_segment_in_pic_flag) { + const guint n = gst_util_ceil_log2 (PicSizeInCtbsY); + + if (pps->dependent_slice_segments_enabled_flag) + READ_UINT8 (&nr, slice->dependent_slice_segment_flag, 1); + /* 7.4.7.1 slice_segment_address parsing */ + READ_UINT32 (&nr, slice->segment_address, n); + } + + if (!slice->dependent_slice_segment_flag) { + for (i = 0; i < pps->num_extra_slice_header_bits; i++) { + if (!nal_reader_skip (&nr, 1)) + goto error; + } + READ_UE_MAX (&nr, slice->type, 63); + + if (pps->output_flag_present_flag) + READ_UINT8 (&nr, slice->pic_output_flag, 1); + if (sps->separate_colour_plane_flag == 1) + READ_UINT8 (&nr, slice->colour_plane_id, 2); + + if (!GST_H265_IS_NAL_TYPE_IDR (nalu->type)) { + const GstH265ShortTermRefPicSet *ref_pic_sets = NULL; + + READ_UINT16 (&nr, slice->pic_order_cnt_lsb, + (sps->log2_max_pic_order_cnt_lsb_minus4 + 4)); + + READ_UINT8 (&nr, slice->short_term_ref_pic_set_sps_flag, 1); + if (!slice->short_term_ref_pic_set_sps_flag) { + guint pos = nal_reader_get_pos (&nr); + guint epb_pos = nal_reader_get_epb_count (&nr); + + if (!gst_h265_parser_parse_short_term_ref_pic_sets + (&slice->short_term_ref_pic_sets, &nr, + sps->num_short_term_ref_pic_sets, sps, sps_ext)) + goto error; + + slice->short_term_ref_pic_set_size = + (nal_reader_get_pos (&nr) - pos) - + (8 * (nal_reader_get_epb_count (&nr) - epb_pos)); + + ref_pic_sets = &slice->short_term_ref_pic_sets; + } else if (sps->num_short_term_ref_pic_sets > 1) { + /* 7.4.7.1 short_term_ref_pic_set_idx */ + const guint n = gst_util_ceil_log2 (sps->num_short_term_ref_pic_sets); + READ_UINT8 (&nr, slice->short_term_ref_pic_set_idx, n); + CHECK_ALLOWED_MAX (slice->short_term_ref_pic_set_idx, + sps->num_short_term_ref_pic_sets - 1); + ref_pic_sets = + &sps->short_term_ref_pic_set[slice->short_term_ref_pic_set_idx]; + } else { + ref_pic_sets = &sps->short_term_ref_pic_set[0]; + } + + if (sps->long_term_ref_pics_present_flag) { + guint32 limit; + guint pos = nal_reader_get_pos (&nr); + guint epb_pos = nal_reader_get_epb_count (&nr); + gint max_num_long_term_pics = 0; + gint TwoVersionsOfCurrDecPicFlag = 0; + + if (sps->num_long_term_ref_pics_sps > 0) { + READ_UE_MAX (&nr, slice->num_long_term_sps, + sps->num_long_term_ref_pics_sps); + } + + /* 7.4.3.3.3 */ + if (pps->pps_scc_extension_flag && + pps->pps_scc_extension_params.pps_curr_pic_ref_enabled_flag && + (sps->sample_adaptive_offset_enabled_flag || + !pps->deblocking_filter_disabled_flag || + pps->deblocking_filter_override_enabled_flag)) { + TwoVersionsOfCurrDecPicFlag = 1; + } + + /* Calculated upper bound num_long_term_pics can have. 7.4.7.1 */ + max_num_long_term_pics = + /* sps_max_dec_pic_buffering_minus1[TemporalId], allowed max is + * MaxDpbSize - 1 */ + MAX_DPB_SIZE - 1 + - (gint) slice->num_long_term_sps + - (gint) ref_pic_sets->NumNegativePics + - (gint) ref_pic_sets->NumPositivePics - + TwoVersionsOfCurrDecPicFlag; + if (max_num_long_term_pics < 0) { + GST_WARNING ("Invalid stream, too many reference pictures"); + goto error; + } + + READ_UE_MAX (&nr, slice->num_long_term_pics, max_num_long_term_pics); + limit = slice->num_long_term_sps + slice->num_long_term_pics; + for (i = 0; i < limit; i++) { + if (i < slice->num_long_term_sps) { + if (sps->num_long_term_ref_pics_sps > 1) { + /* 7.4.7.1 lt_idx_sps */ + const guint n = + gst_util_ceil_log2 (sps->num_long_term_ref_pics_sps); + READ_UINT8 (&nr, slice->lt_idx_sps[i], n); + } + } else { + READ_UINT32 (&nr, slice->poc_lsb_lt[i], + (sps->log2_max_pic_order_cnt_lsb_minus4 + 4)); + READ_UINT8 (&nr, slice->used_by_curr_pic_lt_flag[i], 1); + } + + /* calculate UsedByCurrPicLt */ + if (i < slice->num_long_term_sps) + UsedByCurrPicLt[i] = + sps->used_by_curr_pic_lt_sps_flag[slice->lt_idx_sps[i]]; + else + UsedByCurrPicLt[i] = slice->used_by_curr_pic_lt_flag[i]; + READ_UINT8 (&nr, slice->delta_poc_msb_present_flag[i], 1); + if (slice->delta_poc_msb_present_flag[i]) + READ_UE (&nr, slice->delta_poc_msb_cycle_lt[i]); + } + + slice->long_term_ref_pic_set_size = + (nal_reader_get_pos (&nr) - pos) - + (8 * (nal_reader_get_epb_count (&nr) - epb_pos)); + } + if (sps->temporal_mvp_enabled_flag) + READ_UINT8 (&nr, slice->temporal_mvp_enabled_flag, 1); + } + + if (sps->sample_adaptive_offset_enabled_flag) { + READ_UINT8 (&nr, slice->sao_luma_flag, 1); + if (sps->chroma_array_type) + READ_UINT8 (&nr, slice->sao_chroma_flag, 1); + } + + if (GST_H265_IS_B_SLICE (slice) || GST_H265_IS_P_SLICE (slice)) { + READ_UINT8 (&nr, slice->num_ref_idx_active_override_flag, 1); + + if (slice->num_ref_idx_active_override_flag) { + READ_UE_MAX (&nr, slice->num_ref_idx_l0_active_minus1, 14); + if (GST_H265_IS_B_SLICE (slice)) + READ_UE_MAX (&nr, slice->num_ref_idx_l1_active_minus1, 14); + } else { + /*set default values */ + slice->num_ref_idx_l0_active_minus1 = + pps->num_ref_idx_l0_default_active_minus1; + slice->num_ref_idx_l1_active_minus1 = + pps->num_ref_idx_l1_default_active_minus1; + } + + /* calculate NumPocTotalCurr */ + if (slice->short_term_ref_pic_set_sps_flag) + stRPS = &sps->short_term_ref_pic_set[slice->short_term_ref_pic_set_idx]; + else + stRPS = &slice->short_term_ref_pic_sets; + + for (i = 0; i < stRPS->NumNegativePics; i++) + if (stRPS->UsedByCurrPicS0[i]) + NumPocTotalCurr++; + for (i = 0; i < stRPS->NumPositivePics; i++) + if (stRPS->UsedByCurrPicS1[i]) + NumPocTotalCurr++; + for (i = 0; + i < (slice->num_long_term_sps + slice->num_long_term_pics); i++) + if (UsedByCurrPicLt[i]) + NumPocTotalCurr++; + slice->NumPocTotalCurr = NumPocTotalCurr; + + if (pps->lists_modification_present_flag) { + if (NumPocTotalCurr > 1) + if (!gst_h265_slice_parse_ref_pic_list_modification (slice, &nr, + NumPocTotalCurr)) + goto error; + } + + if (GST_H265_IS_B_SLICE (slice)) + READ_UINT8 (&nr, slice->mvd_l1_zero_flag, 1); + if (pps->cabac_init_present_flag) + READ_UINT8 (&nr, slice->cabac_init_flag, 1); + if (slice->temporal_mvp_enabled_flag) { + if (GST_H265_IS_B_SLICE (slice)) + READ_UINT8 (&nr, slice->collocated_from_l0_flag, 1); + + if ((slice->collocated_from_l0_flag + && slice->num_ref_idx_l0_active_minus1 > 0) + || (!slice->collocated_from_l0_flag + && slice->num_ref_idx_l1_active_minus1 > 0)) { + + /*fixme: add optimization */ + if ((GST_H265_IS_P_SLICE (slice)) + || ((GST_H265_IS_B_SLICE (slice)) + && (slice->collocated_from_l0_flag))) { + READ_UE_MAX (&nr, slice->collocated_ref_idx, + slice->num_ref_idx_l0_active_minus1); + } else if ((GST_H265_IS_B_SLICE (slice)) + && (!slice->collocated_from_l0_flag)) { + READ_UE_MAX (&nr, slice->collocated_ref_idx, + slice->num_ref_idx_l1_active_minus1); + } + } + } + if ((pps->weighted_pred_flag && GST_H265_IS_P_SLICE (slice)) || + (pps->weighted_bipred_flag && GST_H265_IS_B_SLICE (slice))) + if (!gst_h265_slice_parse_pred_weight_table (slice, &nr)) + goto error; + READ_UE_MAX (&nr, slice->five_minus_max_num_merge_cand, 4); + + if (sps->sps_scc_extension_params.motion_vector_resolution_control_idc + == 2) + READ_UINT8 (&nr, slice->use_integer_mv_flag, 1); + } + + READ_SE_ALLOWED (&nr, slice->qp_delta, -87, 77); + if (pps->slice_chroma_qp_offsets_present_flag) { + READ_SE_ALLOWED (&nr, slice->cb_qp_offset, -12, 12); + READ_SE_ALLOWED (&nr, slice->cr_qp_offset, -12, 12); + } + + if (pps->pps_scc_extension_params.pps_slice_act_qp_offsets_present_flag) { + READ_SE_ALLOWED (&nr, slice->slice_act_y_qp_offset, -12, 12); + READ_SE_ALLOWED (&nr, slice->slice_act_cb_qp_offset, -12, 12); + READ_SE_ALLOWED (&nr, slice->slice_act_cr_qp_offset, -12, 12); + } + + if (pps->pps_extension_params.chroma_qp_offset_list_enabled_flag) + READ_UINT8 (&nr, slice->cu_chroma_qp_offset_enabled_flag, 1); + + if (pps->deblocking_filter_override_enabled_flag) + READ_UINT8 (&nr, slice->deblocking_filter_override_flag, 1); + if (slice->deblocking_filter_override_flag) { + READ_UINT8 (&nr, slice->deblocking_filter_disabled_flag, 1); + if (!slice->deblocking_filter_disabled_flag) { + READ_SE_ALLOWED (&nr, slice->beta_offset_div2, -6, 6); + READ_SE_ALLOWED (&nr, slice->tc_offset_div2, -6, 6); + } + } + + if (pps->loop_filter_across_slices_enabled_flag && + (slice->sao_luma_flag || slice->sao_chroma_flag || + !slice->deblocking_filter_disabled_flag)) + READ_UINT8 (&nr, slice->loop_filter_across_slices_enabled_flag, 1); + } + + if (pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag) { + guint32 offset_max; + + if (!pps->tiles_enabled_flag && pps->entropy_coding_sync_enabled_flag) + offset_max = pps->PicHeightInCtbsY - 1; + else if (pps->tiles_enabled_flag && !pps->entropy_coding_sync_enabled_flag) + offset_max = + (pps->num_tile_columns_minus1 + 1) * (pps->num_tile_rows_minus1 + 1) - + 1; + else + offset_max = + (pps->num_tile_columns_minus1 + 1) * pps->PicHeightInCtbsY - 1; + + READ_UE_MAX (&nr, slice->num_entry_point_offsets, offset_max); + if (slice->num_entry_point_offsets > 0) { + READ_UE_MAX (&nr, slice->offset_len_minus1, 31); + slice->entry_point_offset_minus1 = + g_new0 (guint32, slice->num_entry_point_offsets); + for (i = 0; i < slice->num_entry_point_offsets; i++) + READ_UINT32 (&nr, slice->entry_point_offset_minus1[i], + (slice->offset_len_minus1 + 1)); + } + } + + if (pps->slice_segment_header_extension_present_flag) { + guint16 slice_segment_header_extension_length; + READ_UE_MAX (&nr, slice_segment_header_extension_length, 256); + for (i = 0; i < slice_segment_header_extension_length; i++) + if (!nal_reader_skip (&nr, 8)) + goto error; + } + + /* Skip the byte alignment bits */ + if (!nal_reader_skip (&nr, 1)) + goto error; + while (!nal_reader_is_byte_aligned (&nr)) { + if (!nal_reader_skip (&nr, 1)) + goto error; + } + + slice->header_size = nal_reader_get_pos (&nr); + slice->n_emulation_prevention_bytes = nal_reader_get_epb_count (&nr); + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Slice header\""); + + gst_h265_slice_hdr_free (slice); + + return GST_H265_PARSER_ERROR; +} + +/** + * gst_h265_parser_parse_slice_hdr: + * @parser: a #GstH265Parser + * @nalu: The `GST_H265_NAL_SLICE` #GstH265NalUnit to parse + * @slice: The #GstH265SliceHdr to fill. + * + * Parses @data, and fills the @slice structure. + * The resulting @slice_hdr structure shall be deallocated with + * gst_h265_slice_hdr_free() when it is no longer needed + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_parse_slice_hdr (GstH265Parser * parser, + GstH265NalUnit * nalu, GstH265SliceHdr * slice) +{ + GstH265SPSEXT sps_ext; + + return gst_h265_parser_parse_slice_hdr_ext (parser, nalu, slice, &sps_ext); +} + +static gboolean +nal_reader_has_more_data_in_payload (NalReader * nr, + guint32 payload_start_pos_bit, guint32 payloadSize) +{ + if (nal_reader_is_byte_aligned (nr) && + (nal_reader_get_pos (nr) >= (payload_start_pos_bit + 8 * payloadSize))) + return FALSE; + + return TRUE; +} + +static GstH265ParserResult +gst_h265_parser_parse_sei_message (GstH265Parser * parser, + guint8 nal_type, NalReader * nr, GstH265SEIMessage * sei) +{ + guint32 payloadSize; + guint8 payload_type_byte, payload_size_byte; + guint remaining, payload_size; + guint32 payload_start_pos_bit; + GstH265ParserResult res = GST_H265_PARSER_OK; + + GST_DEBUG ("parsing \"Sei message\""); + + memset (sei, 0, sizeof (*sei)); + + do { + READ_UINT8 (nr, payload_type_byte, 8); + sei->payloadType += payload_type_byte; + } while (payload_type_byte == 0xff); + payloadSize = 0; + do { + READ_UINT8 (nr, payload_size_byte, 8); + payloadSize += payload_size_byte; + } + while (payload_size_byte == 0xff); + + remaining = nal_reader_get_remaining (nr); + payload_size = payloadSize * 8 < remaining ? payloadSize * 8 : remaining; + + payload_start_pos_bit = nal_reader_get_pos (nr); + GST_DEBUG + ("SEI message received: payloadType %u, payloadSize = %u bits", + sei->payloadType, payload_size); + + if (nal_type == GST_H265_NAL_PREFIX_SEI) { + switch (sei->payloadType) { + case GST_H265_SEI_BUF_PERIOD: + /* size not set; might depend on emulation_prevention_three_byte */ + res = gst_h265_parser_parse_buffering_period (parser, + &sei->payload.buffering_period, nr); + break; + case GST_H265_SEI_PIC_TIMING: + /* size not set; might depend on emulation_prevention_three_byte */ + res = gst_h265_parser_parse_pic_timing (parser, + &sei->payload.pic_timing, nr); + break; + case GST_H265_SEI_REGISTERED_USER_DATA: + res = gst_h265_parser_parse_registered_user_data (parser, + &sei->payload.registered_user_data, nr, payload_size >> 3); + break; + case GST_H265_SEI_USER_DATA_UNREGISTERED: + res = gst_h265_parser_parse_user_data_unregistered (parser, + &sei->payload.user_data_unregistered, nr, payload_size >> 3); + break; + case GST_H265_SEI_RECOVERY_POINT: + res = gst_h265_parser_parse_recovery_point (parser, + &sei->payload.recovery_point, nr); + break; + case GST_H265_SEI_TIME_CODE: + res = gst_h265_parser_parse_time_code (parser, + &sei->payload.time_code, nr); + break; + case GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME: + res = gst_h265_parser_parse_mastering_display_colour_volume (parser, + &sei->payload.mastering_display_colour_volume, nr); + break; + case GST_H265_SEI_CONTENT_LIGHT_LEVEL: + res = gst_h265_parser_parse_content_light_level_info (parser, + &sei->payload.content_light_level, nr); + break; + default: + /* Just consume payloadSize bytes, which does not account for + emulation prevention bytes */ + if (!nal_reader_skip_long (nr, payload_size)) + goto error; + res = GST_H265_PARSER_OK; + break; + } + } else if (nal_type == GST_H265_NAL_SUFFIX_SEI) { + switch (sei->payloadType) { + default: + /* Just consume payloadSize bytes, which does not account for + emulation prevention bytes */ + if (!nal_reader_skip_long (nr, payload_size)) + goto error; + res = GST_H265_PARSER_OK; + break; + } + } + + /* Not parsing the reserved_payload_extension, but it shouldn't be + * an issue because of 1: There shall not be any reserved_payload_extension + * present in bitstreams conforming to the specification.2. Even though + * it is present, the size will be less than total PayloadSize since the + * size of reserved_payload_extension is supposed to be + * 8 * payloadSize - nEarlierBits - nPayloadZeroBits -1 which means the + * the current implementation will still skip all unnecessary bits correctly. + * In theory, we can have a more optimized implementation by skipping the + * data left in PayLoadSize without out individually checking for each bits, + * since the totoal size will be always less than payloadSize*/ + while (nal_reader_has_more_data_in_payload (nr, payload_start_pos_bit, + payloadSize)) { + /* Skip the byte alignment bits */ + if (!nal_reader_skip (nr, 1)) + goto error; + while (!nal_reader_is_byte_aligned (nr)) { + if (!nal_reader_skip (nr, 1)) + goto error; + } + } + + return res; + +error: + GST_WARNING ("error parsing \"Sei message\""); + gst_h265_sei_free (sei); + return GST_H265_PARSER_ERROR; +} + +/** + * gst_h265_slice_hdr_copy: + * @dst_slice: The destination #GstH265SliceHdr to copy into + * @src_slice: The source #GstH265SliceHdr to copy from + * + * Copies @src_slice into @dst_slice + * + * Returns: %TRUE if everything went fine, %FALSE otherwise + */ +gboolean +gst_h265_slice_hdr_copy (GstH265SliceHdr * dst_slice, + const GstH265SliceHdr * src_slice) +{ + guint i; + + g_return_val_if_fail (dst_slice != NULL, FALSE); + g_return_val_if_fail (src_slice != NULL, FALSE); + + gst_h265_slice_hdr_free (dst_slice); + + *dst_slice = *src_slice; + + if (dst_slice->num_entry_point_offsets > 0) { + dst_slice->entry_point_offset_minus1 = + g_new0 (guint32, dst_slice->num_entry_point_offsets); + for (i = 0; i < dst_slice->num_entry_point_offsets; i++) + dst_slice->entry_point_offset_minus1[i] = + src_slice->entry_point_offset_minus1[i]; + } + + return TRUE; +} + +/** + * gst_h265_slice_hdr_free: + * slice_hdr: The #GstH265SliceHdr to free + * + * Frees @slice_hdr fields. + */ +void +gst_h265_slice_hdr_free (GstH265SliceHdr * slice_hdr) +{ + g_return_if_fail (slice_hdr != NULL); + + if (slice_hdr->num_entry_point_offsets > 0) + g_free (slice_hdr->entry_point_offset_minus1); + slice_hdr->entry_point_offset_minus1 = 0; +} + +/** + * gst_h265_sei_copy: + * @dest_sei: The destination #GstH265SEIMessage to copy into + * @src_sei: The source #GstH265SEIMessage to copy from + * + * Copies @src_sei into @dst_sei + * + * Returns: %TRUE if everything went fine, %FALSE otherwise + */ +gboolean +gst_h265_sei_copy (GstH265SEIMessage * dst_sei, + const GstH265SEIMessage * src_sei) +{ + guint i; + + g_return_val_if_fail (dst_sei != NULL, FALSE); + g_return_val_if_fail (src_sei != NULL, FALSE); + + gst_h265_sei_free (dst_sei); + + *dst_sei = *src_sei; + + if (dst_sei->payloadType == GST_H265_SEI_PIC_TIMING) { + GstH265PicTiming *dst_pic_timing = &dst_sei->payload.pic_timing; + const GstH265PicTiming *src_pic_timing = &src_sei->payload.pic_timing; + + if (dst_pic_timing->num_decoding_units_minus1 > 0) { + dst_pic_timing->num_nalus_in_du_minus1 = + g_new0 (guint32, (dst_pic_timing->num_decoding_units_minus1 + 1)); + dst_pic_timing->du_cpb_removal_delay_increment_minus1 = + g_new0 (guint8, (dst_pic_timing->num_decoding_units_minus1 + 1)); + + for (i = 0; i <= dst_pic_timing->num_decoding_units_minus1; i++) { + dst_pic_timing->num_nalus_in_du_minus1[i] = + src_pic_timing->num_nalus_in_du_minus1[i]; + dst_pic_timing->du_cpb_removal_delay_increment_minus1[i] = + src_pic_timing->du_cpb_removal_delay_increment_minus1[i]; + } + } + } else if (dst_sei->payloadType == GST_H265_SEI_REGISTERED_USER_DATA) { + GstH265RegisteredUserData *dst_rud = &dst_sei->payload.registered_user_data; + const GstH265RegisteredUserData *src_rud = + &src_sei->payload.registered_user_data; + + if (src_rud->size) { + dst_rud->data = g_malloc (src_rud->size); + memcpy ((guint8 *) dst_rud->data, src_rud->data, src_rud->size); + } + } else if (dst_sei->payloadType == GST_H265_SEI_USER_DATA_UNREGISTERED) { + GstH265UserDataUnregistered *dst_udu = + &dst_sei->payload.user_data_unregistered; + const GstH265UserDataUnregistered *src_udu = + &src_sei->payload.user_data_unregistered; + + if (src_udu->size) { + dst_udu->data = g_malloc (src_udu->size); + memcpy ((guint8 *) dst_udu->data, src_udu->data, src_udu->size); + } + } + + return TRUE; +} + +/** + * gst_h265_sei_free: + * sei: The #GstH265SEIMessage to free + * + * Frees @sei fields. + */ +void +gst_h265_sei_free (GstH265SEIMessage * sei) +{ + g_return_if_fail (sei != NULL); + + if (sei->payloadType == GST_H265_SEI_PIC_TIMING) { + GstH265PicTiming *pic_timing = &sei->payload.pic_timing; + if (pic_timing->num_decoding_units_minus1 > 0) { + g_free (pic_timing->num_nalus_in_du_minus1); + g_free (pic_timing->du_cpb_removal_delay_increment_minus1); + } + pic_timing->num_nalus_in_du_minus1 = 0; + pic_timing->du_cpb_removal_delay_increment_minus1 = 0; + } else if (sei->payloadType == GST_H265_SEI_REGISTERED_USER_DATA) { + GstH265RegisteredUserData *rud = &sei->payload.registered_user_data; + g_free ((guint8 *) rud->data); + rud->data = NULL; + } else if (sei->payloadType == GST_H265_SEI_USER_DATA_UNREGISTERED) { + GstH265UserDataUnregistered *udu = &sei->payload.user_data_unregistered; + g_free ((guint8 *) udu->data); + udu->data = NULL; + } +} + +/** + * gst_h265_parser_parse_sei: + * @parser: a #GstH265Parser + * @nalu: The `GST_H265_NAL_*_SEI` #GstH265NalUnit to parse + * @messages: (element-type GstH265SEIMessage): The GArray of #GstH265SEIMessage to fill. The caller must free it when done. + * + * Parses @data, create and fills the @messages array. + * + * Returns: a #GstH265ParserResult + */ +GstH265ParserResult +gst_h265_parser_parse_sei (GstH265Parser * nalparser, GstH265NalUnit * nalu, + GArray ** messages) +{ + NalReader nr; + GstH265SEIMessage sei; + GstH265ParserResult res; + + GST_DEBUG ("parsing SEI nal"); + nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes, + nalu->size - nalu->header_bytes); + *messages = g_array_new (FALSE, FALSE, sizeof (GstH265SEIMessage)); + g_array_set_clear_func (*messages, (GDestroyNotify) gst_h265_sei_free); + + do { + res = gst_h265_parser_parse_sei_message (nalparser, nalu->type, &nr, &sei); + if (res == GST_H265_PARSER_OK) + g_array_append_val (*messages, sei); + else + break; + } while (nal_reader_has_more_data (&nr)); + + return res; +} + +/** + * gst_h265_parser_update_vps: + * @parser: a #GstH265Parser + * @vps: (transfer none): a #GstH265VPS. + * + * Replace internal Video Parameter Set struct corresponding to id of @vps + * with @vps. @nalparser will mark @vps as last parsed vps. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.18 + */ +GstH265ParserResult +gst_h265_parser_update_vps (GstH265Parser * parser, GstH265VPS * vps) +{ + g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (vps != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (vps->id < GST_H265_MAX_VPS_COUNT, + GST_H265_PARSER_ERROR); + + if (!vps->valid) { + GST_WARNING ("Cannot update with invalid VPS"); + return GST_H265_PARSER_ERROR; + } + + GST_DEBUG ("Updating video parameter set with id: %d", vps->id); + + parser->vps[vps->id] = *vps; + parser->last_vps = &parser->vps[vps->id]; + + return GST_H265_PARSER_OK; +} + +/** + * gst_h265_parser_update_sps: + * @parser: a #GstH265Parser + * @sps: (transfer none): a #GstH265SPS. + * + * Replace internal Sequence Parameter Set struct corresponding to id of @sps + * with @sps. @nalparser will mark @sps as last parsed sps. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.18 + */ +GstH265ParserResult +gst_h265_parser_update_sps (GstH265Parser * parser, GstH265SPS * sps) +{ + g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (sps != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (sps->id < GST_H265_MAX_SPS_COUNT, + GST_H265_PARSER_ERROR); + + if (!sps->valid) { + GST_WARNING ("Cannot update with invalid SPS"); + return GST_H265_PARSER_ERROR; + } + + GST_DEBUG ("Updating sequence parameter set with id: %d", sps->id); + + parser->sps[sps->id] = *sps; + parser->last_sps = &parser->sps[sps->id]; + + if (sps->vps) + parser->sps[sps->id].vps = gst_h265_parser_get_vps (parser, sps->vps->id); + + return GST_H265_PARSER_OK; +} + +/** + * gst_h265_parser_update_pps: + * @parser: a #GstH265Parser + * @pps: (transfer none): a #GstH265PPS. + * + * Replace internal Sequence Parameter Set struct corresponding to id of @pps + * with @pps. @nalparser will mark @pps as last parsed sps. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.18 + */ +GstH265ParserResult +gst_h265_parser_update_pps (GstH265Parser * parser, GstH265PPS * pps) +{ + g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (pps != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (pps->id < GST_H265_MAX_PPS_COUNT, + GST_H265_PARSER_ERROR); + + if (!pps->valid) { + GST_WARNING ("Cannot update with invalid PPS"); + return GST_H265_PARSER_ERROR; + } + + GST_DEBUG ("Updating picture parameter set with id: %d", pps->id); + + parser->pps[pps->id] = *pps; + parser->last_pps = &parser->pps[pps->id]; + + if (pps->sps) + parser->pps[pps->id].sps = gst_h265_parser_get_sps (parser, pps->sps->id); + + return GST_H265_PARSER_OK; +} + +/** + * gst_h265_quant_matrix_4x4_get_zigzag_from_raster: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from raster scan order to + * zigzag scan order and store the resulting factors into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_4x4_get_zigzag_from_raster (guint8 out_quant[16], + const guint8 quant[16]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 16; i++) + out_quant[i] = quant[zigzag_4x4[i]]; +} + +/** + * gst_h265_quant_matrix_4x4_get_raster_from_zigzag: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from zigzag scan order to + * raster scan order and store the resulting factors into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_4x4_get_raster_from_zigzag (guint8 out_quant[16], + const guint8 quant[16]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 16; i++) + out_quant[zigzag_4x4[i]] = quant[i]; +} + +/** + * gst_h265_quant_matrix_8x8_get_zigzag_from_raster: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from raster scan order to + * zigzag scan order and store the resulting factors into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_8x8_get_zigzag_from_raster (guint8 out_quant[64], + const guint8 quant[64]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 64; i++) + out_quant[i] = quant[zigzag_8x8[i]]; +} + +/** + * gst_h265_quant_matrix_8x8_get_raster_from_zigzag: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from zigzag scan order to + * raster scan order and store the resulting factors into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_8x8_get_raster_from_zigzag (guint8 out_quant[64], + const guint8 quant[64]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 64; i++) + out_quant[zigzag_8x8[i]] = quant[i]; +} + +/** + * gst_h265_quant_matrix_4x4_get_uprightdiagonal_from_raster: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from raster scan order to + * uprightdiagonal scan order and store the resulting factors + * into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_4x4_get_uprightdiagonal_from_raster (guint8 out_quant[16], + const guint8 quant[16]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 16; i++) + out_quant[i] = quant[uprightdiagonal_4x4[i]]; +} + +/** + * gst_h265_quant_matrix_4x4_get_raster_from_uprightdiagonal: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from uprightdiagonal scan order to + * raster scan order and store the resulting factors into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_4x4_get_raster_from_uprightdiagonal (guint8 out_quant[16], + const guint8 quant[16]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 16; i++) + out_quant[uprightdiagonal_4x4[i]] = quant[i]; +} + +/** + * gst_h265_quant_matrix_8x8_get_uprightdiagonal_from_raster: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from raster scan order to + * uprightdiagonal scan order and store the resulting factors + * into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_8x8_get_uprightdiagonal_from_raster (guint8 out_quant[64], + const guint8 quant[64]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 64; i++) + out_quant[i] = quant[uprightdiagonal_8x8[i]]; +} + +/** + * gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal: + * @out_quant: (out): The resulting quantization matrix + * @quant: The source quantization matrix + * + * Converts quantization matrix @quant from uprightdiagonal scan order to + * raster scan order and store the resulting factors into @out_quant. + * + * Note: it is an error to pass the same table in both @quant and + * @out_quant arguments. + * + * Since: 1.6 + */ +void +gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal (guint8 out_quant[64], + const guint8 quant[64]) +{ + guint i; + + g_return_if_fail (out_quant != quant); + + for (i = 0; i < 64; i++) + out_quant[uprightdiagonal_8x8[i]] = quant[i]; +} + +typedef struct +{ + GstH265Profile profile; + + guint8 max_14bit_constraint_flag; + guint8 max_12bit_constraint_flag; + guint8 max_10bit_constraint_flag; + guint8 max_8bit_constraint_flag; + guint8 max_422chroma_constraint_flag; + guint8 max_420chroma_constraint_flag; + guint8 max_monochrome_constraint_flag; + guint8 intra_constraint_flag; + guint8 one_picture_only_constraint_flag; + gboolean lower_bit_rate_constraint_flag_set; + + /* Tie breaker if more than one profiles are matching */ + guint priority; +} H265ExtensionProfile; + +typedef struct +{ + H265ExtensionProfile *profile; + guint extra_constraints; +} H265ExtensionProfileMatch; + +static gint +sort_fre_profile_matches (H265ExtensionProfileMatch * a, + H265ExtensionProfileMatch * b) +{ + gint d; + + d = a->extra_constraints - b->extra_constraints; + if (d) + return d; + + return b->profile->priority - a->profile->priority; +} + +static GstH265Profile +get_extension_profile (H265ExtensionProfile * profiles, guint num, + const GstH265ProfileTierLevel * ptl) +{ + GstH265Profile result = GST_H265_PROFILE_INVALID; + guint i; + GList *matches = NULL; + + for (i = 0; i < num; i++) { + H265ExtensionProfile p = profiles[i]; + guint extra_constraints = 0; + H265ExtensionProfileMatch *m; + + /* Filter out all the profiles having constraints not satisfied by @ptl. + * Then pick the one having the least extra constraints. This allow us + * to match the closest profile if bitstream contains not standard + * constraints. */ + if (p.max_14bit_constraint_flag != ptl->max_14bit_constraint_flag) { + if (p.max_14bit_constraint_flag) + continue; + extra_constraints++; + } + + if (p.max_12bit_constraint_flag != ptl->max_12bit_constraint_flag) { + if (p.max_12bit_constraint_flag) + continue; + extra_constraints++; + } + + if (p.max_10bit_constraint_flag != ptl->max_10bit_constraint_flag) { + if (p.max_10bit_constraint_flag) + continue; + extra_constraints++; + } + + if (p.max_8bit_constraint_flag != ptl->max_8bit_constraint_flag) { + if (p.max_8bit_constraint_flag) + continue; + extra_constraints++; + } + + if (p.max_422chroma_constraint_flag != ptl->max_422chroma_constraint_flag) { + if (p.max_422chroma_constraint_flag) + continue; + extra_constraints++; + } + + if (p.max_420chroma_constraint_flag != ptl->max_420chroma_constraint_flag) { + if (p.max_420chroma_constraint_flag) + continue; + extra_constraints++; + } + + if (p.max_monochrome_constraint_flag != ptl->max_monochrome_constraint_flag) { + if (p.max_monochrome_constraint_flag) + continue; + extra_constraints++; + } + + if (p.intra_constraint_flag != ptl->intra_constraint_flag) { + if (p.intra_constraint_flag) + continue; + extra_constraints++; + } + + if (p.one_picture_only_constraint_flag != + ptl->one_picture_only_constraint_flag) { + if (p.one_picture_only_constraint_flag) + continue; + extra_constraints++; + } + + if (p.lower_bit_rate_constraint_flag_set + && !ptl->lower_bit_rate_constraint_flag) + continue; + + if (extra_constraints == 0) { + result = p.profile; + break; + } + + m = g_new0 (H265ExtensionProfileMatch, 1); + m->profile = &profiles[i]; + m->extra_constraints = extra_constraints; + matches = g_list_prepend (matches, m); + } + + if (result == GST_H265_PROFILE_INVALID && matches) { + H265ExtensionProfileMatch *m; + + matches = g_list_sort (matches, (GCompareFunc) sort_fre_profile_matches); + m = matches->data; + result = m->profile->profile; + GST_INFO ("Fail to find the profile matches all extensions bits," + " select the closest %s with %d bit diff", + gst_h265_profile_to_string (result), m->extra_constraints); + } + + if (matches) + g_list_free_full (matches, g_free); + + return result; +} + +static GstH265Profile +get_format_range_extension_profile (const GstH265ProfileTierLevel * ptl) +{ + /* Profile idc: GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION + See Table A.2 for the definition of those formats */ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_MONOCHROME, + 0, 1, 1, 1, 1, 1, 1, 0, 0, TRUE, 0}, + {GST_H265_PROFILE_MONOCHROME_10, + 0, 1, 1, 0, 1, 1, 1, 0, 0, TRUE, 1}, + {GST_H265_PROFILE_MONOCHROME_12, + 0, 1, 0, 0, 1, 1, 1, 0, 0, TRUE, 2}, + {GST_H265_PROFILE_MONOCHROME_16, + 0, 0, 0, 0, 1, 1, 1, 0, 0, TRUE, 3}, + {GST_H265_PROFILE_MAIN_12, + 0, 1, 0, 0, 1, 1, 0, 0, 0, TRUE, 4}, + {GST_H265_PROFILE_MAIN_422_10, + 0, 1, 1, 0, 1, 0, 0, 0, 0, TRUE, 5}, + {GST_H265_PROFILE_MAIN_422_12, + 0, 1, 0, 0, 1, 0, 0, 0, 0, TRUE, 6}, + {GST_H265_PROFILE_MAIN_444, + 0, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 7}, + {GST_H265_PROFILE_MAIN_444_10, + 0, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 8}, + {GST_H265_PROFILE_MAIN_444_12, + 0, 1, 0, 0, 0, 0, 0, 0, 0, TRUE, 9}, + {GST_H265_PROFILE_MAIN_INTRA, + 0, 1, 1, 1, 1, 1, 0, 1, 0, FALSE, 10}, + {GST_H265_PROFILE_MAIN_10_INTRA, + 0, 1, 1, 0, 1, 1, 0, 1, 0, FALSE, 11}, + {GST_H265_PROFILE_MAIN_12_INTRA, + 0, 1, 0, 0, 1, 1, 0, 1, 0, FALSE, 12}, + {GST_H265_PROFILE_MAIN_422_10_INTRA, + 0, 1, 1, 0, 1, 0, 0, 1, 0, FALSE, 13}, + {GST_H265_PROFILE_MAIN_422_12_INTRA, + 0, 1, 0, 0, 1, 0, 0, 1, 0, FALSE, 14}, + {GST_H265_PROFILE_MAIN_444_INTRA, + 0, 1, 1, 1, 0, 0, 0, 1, 0, FALSE, 15}, + {GST_H265_PROFILE_MAIN_444_10_INTRA, + 0, 1, 1, 0, 0, 0, 0, 1, 0, FALSE, 16}, + {GST_H265_PROFILE_MAIN_444_12_INTRA, + 0, 1, 0, 0, 0, 0, 0, 1, 0, FALSE, 17}, + {GST_H265_PROFILE_MAIN_444_16_INTRA, + 0, 0, 0, 0, 0, 0, 0, 1, 0, FALSE, 18}, + {GST_H265_PROFILE_MAIN_444_STILL_PICTURE, + 0, 1, 1, 1, 0, 0, 0, 1, 1, FALSE, 19}, + {GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE, + 0, 0, 0, 0, 0, 0, 0, 1, 1, FALSE, 20}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile +get_3d_profile (const GstH265ProfileTierLevel * ptl) +{ + /* profile idc: GST_H265_PROFILE_IDC_3D_MAIN */ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_3D_MAIN, + 0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile +get_multiview_profile (const GstH265ProfileTierLevel * ptl) +{ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_MULTIVIEW_MAIN, + 0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile +get_scalable_profile (const GstH265ProfileTierLevel * ptl) +{ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_SCALABLE_MAIN, + 0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0}, + {GST_H265_PROFILE_SCALABLE_MAIN_10, + 0, 1, 1, 0, 1, 1, 0, 0, 0, TRUE, 1}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile +get_high_throughput_profile (const GstH265ProfileTierLevel * ptl) +{ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_HIGH_THROUGHPUT_444, + 1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 0}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444_10, + 1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 1}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444_14, + 1, 0, 0, 0, 0, 0, 0, 0, 0, TRUE, 2}, + {GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA, + 0, 0, 0, 0, 0, 0, 0, 1, 0, FALSE, 3}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile +get_screen_content_coding_extensions_profile (const GstH265ProfileTierLevel * + ptl) +{ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN, + 1, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10, + 1, 1, 1, 0, 1, 1, 0, 0, 0, TRUE, 1}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444, + 1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 2}, + {GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10, + 1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 3}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile +get_scalable_format_range_extensions_profile (const GstH265ProfileTierLevel * + ptl) +{ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_SCALABLE_MONOCHROME, + 1, 1, 1, 1, 1, 1, 1, 0, 0, TRUE, 0}, + {GST_H265_PROFILE_SCALABLE_MONOCHROME_12, + 1, 1, 0, 0, 1, 1, 1, 0, 0, TRUE, 1}, + {GST_H265_PROFILE_SCALABLE_MONOCHROME_16, + 0, 0, 0, 0, 1, 1, 1, 0, 0, TRUE, 2}, + {GST_H265_PROFILE_SCALABLE_MAIN_444, + 1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 3}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static GstH265Profile + get_screen_content_coding_extensions_high_throughput_profile + (const GstH265ProfileTierLevel * ptl) +{ + static H265ExtensionProfile profiles[] = { + {GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444, + 1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 0}, + {GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10, + 1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 1}, + {GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14, + 1, 0, 0, 0, 0, 0, 0, 0, 0, TRUE, 2}, + }; + + return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl); +} + +static inline void +append_profile (GstH265Profile profiles[GST_H265_PROFILE_MAX], guint * idx, + GstH265Profile profile) +{ + if (profile == GST_H265_PROFILE_INVALID) + return; + profiles[*idx] = profile; + (*idx)++; +} + +/* *INDENT-OFF* */ +struct h265_profiles_map +{ + GstH265ProfileIDC profile_idc; + GstH265Profile (*get_profile) (const GstH265ProfileTierLevel *); + GstH265Profile profile; +}; +/* *INDENT-ON* */ + +static const struct h265_profiles_map profiles_map[] = { + /* keep profile check in asc order */ + {GST_H265_PROFILE_IDC_MAIN, NULL, GST_H265_PROFILE_MAIN}, + {GST_H265_PROFILE_IDC_MAIN_10, NULL, GST_H265_PROFILE_MAIN_10}, + {GST_H265_PROFILE_IDC_MAIN_STILL_PICTURE, NULL, + GST_H265_PROFILE_MAIN_STILL_PICTURE}, + {GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION, + get_format_range_extension_profile, GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_HIGH_THROUGHPUT, get_high_throughput_profile, + GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_MULTIVIEW_MAIN, get_multiview_profile, + GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_SCALABLE_MAIN, get_scalable_profile, + GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_3D_MAIN, get_3d_profile, GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING, + get_screen_content_coding_extensions_profile, + GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION, + get_scalable_format_range_extensions_profile, + GST_H265_PROFILE_INVALID}, + {GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION, + get_screen_content_coding_extensions_high_throughput_profile, + GST_H265_PROFILE_INVALID}, +}; + +static void +gst_h265_profile_tier_level_get_profiles (const GstH265ProfileTierLevel * ptl, + GstH265Profile profiles[GST_H265_PROFILE_MAX], guint * len) +{ + guint i = 0, j; + + /* First add profile idc */ + for (j = 0; j < G_N_ELEMENTS (profiles_map); j++) { + if (ptl->profile_idc == profiles_map[j].profile_idc) { + if (profiles_map[j].get_profile) + append_profile (profiles, &i, profiles_map[j].get_profile (ptl)); + else + profiles[i++] = profiles_map[j].profile; + break; + } + } + + /* Later add compatibility flags */ + for (j = 0; j < G_N_ELEMENTS (profiles_map); j++) { + if (i > 0 && ptl->profile_idc == profiles_map[j].profile_idc) + continue; + if (ptl->profile_compatibility_flag[profiles_map[j].profile_idc]) { + if (profiles_map[j].get_profile) + append_profile (profiles, &i, profiles_map[j].get_profile (ptl)); + else + profiles[i++] = profiles_map[j].profile; + } + } + + *len = i; +} + +/** + * gst_h265_profile_tier_level_get_profile: + * @ptl: a #GstH265ProfileTierLevel + * + * Return the H265 profile defined in @ptl. + * + * Returns: a #GstH265Profile + * Since: 1.14 + */ +GstH265Profile +gst_h265_profile_tier_level_get_profile (const GstH265ProfileTierLevel * ptl) +{ + guint len = 0; + GstH265Profile profiles[GST_H265_PROFILE_MAX] = { GST_H265_PROFILE_INVALID, }; + + gst_h265_profile_tier_level_get_profiles (ptl, profiles, &len); + + if (len > 0) + return profiles[0]; + + return GST_H265_PROFILE_INVALID; +} + +/** + * gst_h265_profile_to_string: + * @profile: a #GstH265Profile + * + * Returns the descriptive name for the #GstH265Profile. + * + * Returns: (nullable): the name for @profile or %NULL on error + * + * Since: 1.18 + */ +const gchar * +gst_h265_profile_to_string (GstH265Profile profile) +{ + guint i; + + if (profile == GST_H265_PROFILE_INVALID || profile == GST_H265_PROFILE_MAX) + return NULL; + + for (i = 0; i < G_N_ELEMENTS (h265_profiles); i++) { + if (profile == h265_profiles[i].profile) + return h265_profiles[i].name; + } + + return NULL; +} + +/** + * gst_h265_profile_from_string: + * @string: the descriptive name for #GstH265Profile + * + * Returns a #GstH265Profile for the @string. + * + * Returns: the #GstH265Profile of @string or %GST_H265_PROFILE_INVALID on error + * + * Since: 1.18 + */ +GstH265Profile +gst_h265_profile_from_string (const gchar * string) +{ + guint i; + + if (string == NULL) + return GST_H265_PROFILE_INVALID; + + for (i = 0; i < G_N_ELEMENTS (h265_profiles); i++) { + if (g_strcmp0 (string, h265_profiles[i].name) == 0) { + return h265_profiles[i].profile; + } + } + + return GST_H265_PROFILE_INVALID; +} + +/** + * gst_h265_slice_type_to_string: + * @slice_type: a #GstH265SliceType + * + * Returns the descriptive name for the #GstH265SliceType. + * + * Returns: (nullable): the name for @slice_type or %NULL on error + * + * Since: 1.24 + */ +const gchar * +gst_h265_slice_type_to_string (GstH265SliceType slice_type) +{ + switch (slice_type) { + case GST_H265_P_SLICE: + return "P"; + case GST_H265_B_SLICE: + return "B"; + case GST_H265_I_SLICE: + return "I"; + default: + GST_ERROR ("unknown %d slice type", slice_type); + } + + return NULL; +} + +static gboolean +gst_h265_write_sei_registered_user_data (NalWriter * nw, + GstH265RegisteredUserData * rud) +{ + WRITE_UINT8 (nw, rud->country_code, 8); + if (rud->country_code == 0xff) + WRITE_UINT8 (nw, rud->country_code_extension, 8); + + WRITE_BYTES (nw, rud->data, rud->size); + + return TRUE; + +error: + return FALSE; +} + +static gboolean +gst_h265_write_sei_user_data_unregistered (NalWriter * nw, + GstH265UserDataUnregistered * udu) +{ + WRITE_BYTES (nw, udu->uuid, 16); + WRITE_BYTES (nw, udu->data, udu->size); + + return TRUE; + +error: + return FALSE; +} + +static gboolean +gst_h265_write_sei_time_code (NalWriter * nw, GstH265TimeCode * tc) +{ + gint i; + + WRITE_UINT8 (nw, tc->num_clock_ts, 2); + + for (i = 0; i < tc->num_clock_ts; i++) { + WRITE_UINT8 (nw, tc->clock_timestamp_flag[i], 1); + if (tc->clock_timestamp_flag[i]) { + WRITE_UINT8 (nw, tc->units_field_based_flag[i], 1); + WRITE_UINT8 (nw, tc->counting_type[i], 5); + WRITE_UINT8 (nw, tc->full_timestamp_flag[i], 1); + WRITE_UINT8 (nw, tc->discontinuity_flag[i], 1); + WRITE_UINT8 (nw, tc->cnt_dropped_flag[i], 1); + WRITE_UINT16 (nw, tc->n_frames[i], 9); + + if (tc->full_timestamp_flag[i]) { + WRITE_UINT8 (nw, tc->seconds_value[i], 6); + WRITE_UINT8 (nw, tc->minutes_value[i], 6); + WRITE_UINT8 (nw, tc->hours_value[i], 5); + } else { + WRITE_UINT8 (nw, tc->seconds_flag[i], 1); + if (tc->seconds_flag[i]) { + WRITE_UINT8 (nw, tc->seconds_value[i], 6); + WRITE_UINT8 (nw, tc->minutes_flag[i], 1); + if (tc->minutes_flag[i]) { + WRITE_UINT8 (nw, tc->minutes_value[i], 6); + WRITE_UINT8 (nw, tc->hours_flag[i], 1); + if (tc->hours_flag[i]) { + WRITE_UINT8 (nw, tc->hours_value[i], 5); + } + } + } + } + } + + WRITE_UINT8 (nw, tc->time_offset_length[i], 5); + + if (tc->time_offset_length[i] > 0) + WRITE_UINT8 (nw, tc->time_offset_value[i], tc->time_offset_length[i]); + } + + return TRUE; + +error: + return FALSE; +} + +static gboolean +gst_h265_write_sei_mastering_display_colour_volume (NalWriter * nw, + GstH265MasteringDisplayColourVolume * mdcv) +{ + gint i; + + for (i = 0; i < 3; i++) { + WRITE_UINT16 (nw, mdcv->display_primaries_x[i], 16); + WRITE_UINT16 (nw, mdcv->display_primaries_y[i], 16); + } + + WRITE_UINT16 (nw, mdcv->white_point_x, 16); + WRITE_UINT16 (nw, mdcv->white_point_y, 16); + WRITE_UINT32 (nw, mdcv->max_display_mastering_luminance, 32); + WRITE_UINT32 (nw, mdcv->min_display_mastering_luminance, 32); + + return TRUE; + +error: + return FALSE; +} + +static gboolean +gst_h265_write_sei_content_light_level_info (NalWriter * nw, + GstH265ContentLightLevel * cll) +{ + WRITE_UINT16 (nw, cll->max_content_light_level, 16); + WRITE_UINT16 (nw, cll->max_pic_average_light_level, 16); + + return TRUE; + +error: + return FALSE; +} + +static GstMemory * +gst_h265_create_sei_memory_internal (guint8 layer_id, guint8 temporal_id_plus1, + guint nal_prefix_size, gboolean packetized, GArray * messages) +{ + NalWriter nw; + gint i; + gboolean have_written_data = FALSE; + + nal_writer_init (&nw, nal_prefix_size, packetized); + + if (messages->len == 0) + goto error; + + GST_DEBUG ("Create SEI nal from array, len: %d", messages->len); + + /* nal header */ + /* forbidden_zero_bit */ + WRITE_UINT8 (&nw, 0, 1); + /* nal_unit_type */ + WRITE_UINT8 (&nw, GST_H265_NAL_PREFIX_SEI, 6); + /* nuh_layer_id */ + WRITE_UINT8 (&nw, layer_id, 6); + /* nuh_temporal_id_plus1 */ + WRITE_UINT8 (&nw, temporal_id_plus1, 3); + + for (i = 0; i < messages->len; i++) { + GstH265SEIMessage *msg = &g_array_index (messages, GstH265SEIMessage, i); + guint32 payload_size_data = 0; + guint32 payload_size_in_bits = 0; + guint32 payload_type_data = msg->payloadType; + gboolean need_align = FALSE; + + switch (payload_type_data) { + case GST_H265_SEI_REGISTERED_USER_DATA:{ + GstH265RegisteredUserData *rud = &msg->payload.registered_user_data; + + /* itu_t_t35_country_code: 8 bits */ + payload_size_data = 1; + if (rud->country_code == 0xff) { + /* itu_t_t35_country_code_extension_byte */ + payload_size_data++; + } + + payload_size_data += rud->size; + break; + } + case GST_H265_SEI_USER_DATA_UNREGISTERED:{ + GstH265UserDataUnregistered *udu = &msg->payload.user_data_unregistered; + + payload_size_data = 16 + udu->size; + break; + } + case GST_H265_SEI_TIME_CODE:{ + gint j; + GstH265TimeCode *tc = &msg->payload.time_code; + /* num_clock_ts: 2 bits */ + payload_size_in_bits = 2; + for (j = 0; j < tc->num_clock_ts; j++) { + /* clock_timestamp_flag: 1 bit */ + payload_size_in_bits += 1; + + if (tc->clock_timestamp_flag[j]) { + /* units_field_based_flag: 1 bit + * counting_type: 5 bits + * full_timestamp_flag: 1 bit + * discontinuity_flag: 1 bit + * cnt_dropped_flag: 1 bit + * n_frames: 9 bit + */ + payload_size_in_bits += 18; + + if (tc->full_timestamp_flag[j]) { + /* seconds_value: 6 bits + * minutes_value: 6 bits + * hours_value: 5 bits + */ + payload_size_in_bits += 17; + } else { + /* seconds_flag: 1 bit */ + payload_size_in_bits += 1; + + if (tc->seconds_flag[j]) { + /* seconds_value: 6 bits + * minutes_flag: 1 bit + */ + payload_size_in_bits += 7; + + if (tc->minutes_flag[j]) { + /* minutes_value: 6 bits + * hours_flag: 1 bit + */ + payload_size_in_bits += 7; + if (tc->hours_flag[j]) { + /* hours_value: 5 bits */ + payload_size_in_bits += 5; + } + } + } + } + + /* time_offset_length: 5bits + * time_offset_value: time_offset_length bits + */ + payload_size_in_bits += (5 + tc->time_offset_length[j]); + } + } + + payload_size_data = payload_size_in_bits >> 3; + + if ((payload_size_in_bits & 0x7) != 0) { + GST_INFO ("Bits for Time Code SEI is not byte aligned"); + payload_size_data++; + need_align = TRUE; + } + break; + } + case GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME: + /* x, y 16 bits per RGB channel + * x, y 16 bits white point + * max, min luminance 32 bits + * + * (2 * 2 * 3) + (2 * 2) + (4 * 2) = 24 bytes + */ + payload_size_data = 24; + break; + case GST_H265_SEI_CONTENT_LIGHT_LEVEL: + /* maxCLL and maxFALL per 16 bits + * + * 2 * 2 = 4 bytes + */ + payload_size_data = 4; + break; + default: + break; + } + + if (payload_size_data == 0) { + GST_FIXME ("Unsupported SEI type %d", msg->payloadType); + continue; + } + + /* write payload type bytes */ + while (payload_type_data >= 0xff) { + WRITE_UINT8 (&nw, 0xff, 8); + payload_type_data -= 0xff; + } + WRITE_UINT8 (&nw, payload_type_data, 8); + + /* write payload size bytes */ + while (payload_size_data >= 0xff) { + WRITE_UINT8 (&nw, 0xff, 8); + payload_size_data -= 0xff; + } + WRITE_UINT8 (&nw, payload_size_data, 8); + + switch (msg->payloadType) { + case GST_H265_SEI_REGISTERED_USER_DATA: + GST_DEBUG ("Writing \"Registered user data\" done"); + if (!gst_h265_write_sei_registered_user_data (&nw, + &msg->payload.registered_user_data)) { + GST_WARNING ("Failed to write \"Registered user data\""); + goto error; + } + have_written_data = TRUE; + break; + case GST_H265_SEI_USER_DATA_UNREGISTERED: + GST_DEBUG ("Writing \"Unregistered user data\" done"); + if (!gst_h265_write_sei_user_data_unregistered (&nw, + &msg->payload.user_data_unregistered)) { + GST_WARNING ("Failed to write \"Unregistered user data\""); + goto error; + } + have_written_data = TRUE; + break; + case GST_H265_SEI_TIME_CODE: + GST_DEBUG ("Wrtiting \"Time code\""); + if (!gst_h265_write_sei_time_code (&nw, &msg->payload.time_code)) { + GST_WARNING ("Failed to write \"Time code\""); + goto error; + } + have_written_data = TRUE; + break; + case GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME: + GST_DEBUG ("Wrtiting \"Mastering display colour volume\""); + if (!gst_h265_write_sei_mastering_display_colour_volume (&nw, + &msg->payload.mastering_display_colour_volume)) { + GST_WARNING ("Failed to write \"Mastering display colour volume\""); + goto error; + } + have_written_data = TRUE; + break; + case GST_H265_SEI_CONTENT_LIGHT_LEVEL: + GST_DEBUG ("Writing \"Content light level\" done"); + if (!gst_h265_write_sei_content_light_level_info (&nw, + &msg->payload.content_light_level)) { + GST_WARNING ("Failed to write \"Content light level\""); + goto error; + } + have_written_data = TRUE; + break; + default: + break; + } + + if (need_align && !nal_writer_do_rbsp_trailing_bits (&nw)) { + GST_WARNING ("Cannot insert traling bits"); + goto error; + } + } + + if (!have_written_data) { + GST_WARNING ("No written sei data"); + goto error; + } + + if (!nal_writer_do_rbsp_trailing_bits (&nw)) { + GST_WARNING ("Failed to insert rbsp trailing bits"); + goto error; + } + + return nal_writer_reset_and_get_memory (&nw); + +error: + nal_writer_reset (&nw); + + return NULL; +} + +/** + * gst_h265_create_sei_memory: + * @layer_id: a nal unit layer id + * @temporal_id_plus1: a nal unit temporal identifier + * @start_code_prefix_length: a length of start code prefix, must be 3 or 4 + * @messages: (element-type GstH265SEIMessage) (transfer none): a GArray of #GstH265SEIMessage + * + * Creates raw byte-stream format (a.k.a Annex B type) SEI nal unit data + * from @messages + * + * Returns: a #GstMemory containing a PREFIX SEI nal unit + * + * Since: 1.18 + */ +GstMemory * +gst_h265_create_sei_memory (guint8 layer_id, guint8 temporal_id_plus1, + guint8 start_code_prefix_length, GArray * messages) +{ + g_return_val_if_fail (start_code_prefix_length == 3 + || start_code_prefix_length == 4, NULL); + g_return_val_if_fail (messages != NULL, NULL); + g_return_val_if_fail (messages->len > 0, NULL); + + return gst_h265_create_sei_memory_internal (layer_id, temporal_id_plus1, + start_code_prefix_length, FALSE, messages); +} + +/** + * gst_h265_create_sei_memory_hevc: + * @layer_id: a nal unit layer id + * @temporal_id_plus1: a nal unit temporal identifier + * @nal_length_size: a size of nal length field, allowed range is [1, 4] + * @messages: (element-type GstH265SEIMessage) (transfer none): a GArray of #GstH265SEIMessage + * + * Creates raw packetized format SEI nal unit data from @messages + * + * Returns: a #GstMemory containing a PREFIX SEI nal unit + * + * Since: 1.18 + */ +GstMemory * +gst_h265_create_sei_memory_hevc (guint8 layer_id, guint8 temporal_id_plus1, + guint8 nal_length_size, GArray * messages) +{ + return gst_h265_create_sei_memory_internal (layer_id, temporal_id_plus1, + nal_length_size, TRUE, messages); +} + +static GstBuffer * +gst_h265_parser_insert_sei_internal (GstH265Parser * parser, + guint8 nal_prefix_size, gboolean packetized, GstBuffer * au, + GstMemory * sei) +{ + GstH265NalUnit nalu; + GstH265NalUnit sei_nalu; + GstMapInfo info; + GstMapInfo sei_info; + GstH265ParserResult pres; + guint offset = 0; + GstBuffer *new_buffer = NULL; + GstMemory *new_mem = NULL; + + /* all SEI payload types supported by us need to have the identical + * temporal id to that of slice. Parse SEI first and we will + * update it if it's required */ + if (!gst_memory_map (sei, &sei_info, GST_MAP_READ)) { + GST_ERROR ("Cannot map sei memory"); + return NULL; + } + + if (packetized) { + pres = gst_h265_parser_identify_nalu_hevc (parser, + sei_info.data, 0, sei_info.size, nal_prefix_size, &sei_nalu); + } else { + pres = gst_h265_parser_identify_nalu (parser, + sei_info.data, 0, sei_info.size, &sei_nalu); + } + gst_memory_unmap (sei, &sei_info); + if (pres != GST_H265_PARSER_OK && pres != GST_H265_PARSER_NO_NAL_END) { + GST_DEBUG ("Failed to identify sei nal unit, ret: %d", pres); + return NULL; + } + + if (!gst_buffer_map (au, &info, GST_MAP_READ)) { + GST_ERROR ("Cannot map au buffer"); + return NULL; + } + + /* Find the offset of the first slice */ + do { + if (packetized) { + pres = gst_h265_parser_identify_nalu_hevc (parser, + info.data, offset, info.size, nal_prefix_size, &nalu); + } else { + pres = gst_h265_parser_identify_nalu (parser, + info.data, offset, info.size, &nalu); + } + + if (pres != GST_H265_PARSER_OK && pres != GST_H265_PARSER_NO_NAL_END) { + GST_DEBUG ("Failed to identify nal unit, ret: %d", pres); + gst_buffer_unmap (au, &info); + + return NULL; + } + + if (nalu.type <= GST_H265_NAL_SLICE_RASL_R + || (nalu.type >= GST_H265_NAL_SLICE_BLA_W_LP + && nalu.type <= GST_H265_NAL_SLICE_CRA_NUT)) { + GST_DEBUG ("Found slice nal type %d at offset %d", nalu.type, + nalu.sc_offset); + break; + } + + offset = nalu.offset + nalu.size; + } while (pres == GST_H265_PARSER_OK); + gst_buffer_unmap (au, &info); + + /* found the best position now, create new buffer */ + new_buffer = gst_buffer_new (); + + /* copy all metadata */ + if (!gst_buffer_copy_into (new_buffer, au, GST_BUFFER_COPY_METADATA, 0, -1)) { + GST_ERROR ("Failed to copy metadata into new buffer"); + gst_clear_buffer (&new_buffer); + goto out; + } + + /* copy non-slice nal */ + if (nalu.sc_offset > 0) { + if (!gst_buffer_copy_into (new_buffer, au, + GST_BUFFER_COPY_MEMORY, 0, nalu.sc_offset)) { + GST_ERROR ("Failed to copy buffer"); + gst_clear_buffer (&new_buffer); + goto out; + } + } + + /* check whether we need to update temporal id and layer id. + * If it's not matched to slice nalu, update it. + */ + if (sei_nalu.layer_id != nalu.layer_id || sei_nalu.temporal_id_plus1 != + nalu.temporal_id_plus1) { + guint16 nalu_header; + guint16 layer_id_temporal_id = 0; + new_mem = gst_memory_copy (sei, 0, -1); + + if (!gst_memory_map (new_mem, &sei_info, GST_MAP_READWRITE)) { + GST_ERROR ("Failed to map new sei memory"); + gst_memory_unref (new_mem); + gst_clear_buffer (&new_buffer); + goto out; + } + + nalu_header = GST_READ_UINT16_BE (sei_info.data + sei_nalu.offset); + + /* clear bits 7 ~ 15 + * NOTE: + * bit 0: forbidden_zero_bit + * bits 1 ~ 6: nalu type */ + nalu_header &= 0xfe00; + + layer_id_temporal_id = ((nalu.layer_id << 3) & 0x1f8); + layer_id_temporal_id |= (nalu.temporal_id_plus1 & 0x7); + + nalu_header |= layer_id_temporal_id; + GST_WRITE_UINT16_BE (sei_info.data + sei_nalu.offset, nalu_header); + gst_memory_unmap (new_mem, &sei_info); + } else { + new_mem = gst_memory_ref (sei); + } + + /* insert sei */ + gst_buffer_append_memory (new_buffer, new_mem); + + /* copy the rest */ + if (!gst_buffer_copy_into (new_buffer, au, + GST_BUFFER_COPY_MEMORY, nalu.sc_offset, -1)) { + GST_ERROR ("Failed to copy buffer"); + gst_clear_buffer (&new_buffer); + goto out; + } + +out: + return new_buffer; +} + +/** + * gst_h265_parser_insert_sei: + * @parser: a #GstH265Parser + * @au: (transfer none): a #GstBuffer containing AU data + * @sei: (transfer none): a #GstMemory containing a SEI nal + * + * Copy @au into new #GstBuffer and insert @sei into the #GstBuffer. + * The validation for completeness of @au and @sei is caller's responsibility. + * Both @au and @sei must be byte-stream formatted + * + * Returns: (transfer full) (nullable): a SEI inserted #GstBuffer or %NULL + * if cannot figure out proper position to insert a @sei + * + * Since: 1.18 + */ +GstBuffer * +gst_h265_parser_insert_sei (GstH265Parser * parser, GstBuffer * au, + GstMemory * sei) +{ + g_return_val_if_fail (parser != NULL, NULL); + g_return_val_if_fail (GST_IS_BUFFER (au), NULL); + g_return_val_if_fail (sei != NULL, NULL); + + /* the size of start code prefix (3 or 4) is not matter since it will be + * scanned */ + return gst_h265_parser_insert_sei_internal (parser, 4, FALSE, au, sei); +} + +/** + * gst_h265_parser_insert_sei_hevc: + * @parser: a #GstH265Parser + * @nal_length_size: a size of nal length field, allowed range is [1, 4] + * @au: (transfer none): a #GstBuffer containing AU data + * @sei: (transfer none): a #GstMemory containing a SEI nal + * + * Copy @au into new #GstBuffer and insert @sei into the #GstBuffer. + * The validation for completeness of @au and @sei is caller's responsibility. + * Nal prefix type of both @au and @sei must be packetized, and + * also the size of nal length field must be identical to @nal_length_size + * + * Returns: (transfer full) (nullable): a SEI inserted #GstBuffer or %NULL + * if cannot figure out proper position to insert a @sei + * + * Since: 1.18 + */ +GstBuffer * +gst_h265_parser_insert_sei_hevc (GstH265Parser * parser, guint8 nal_length_size, + GstBuffer * au, GstMemory * sei) +{ + g_return_val_if_fail (parser != NULL, NULL); + g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5, NULL); + g_return_val_if_fail (GST_IS_BUFFER (au), NULL); + g_return_val_if_fail (sei != NULL, NULL); + + return gst_h265_parser_insert_sei_internal (parser, nal_length_size, TRUE, + au, sei); +} + +/** + * gst_h265_get_profile_from_sps: + * @sps: a #GstH265SPS + * + * Return the H265 profile from @sps. + * + * Returns: a #GstH265Profile + * Since: 1.20 + */ +GstH265Profile +gst_h265_get_profile_from_sps (GstH265SPS * sps) +{ + GstH265Profile profiles[GST_H265_PROFILE_MAX] = { GST_H265_PROFILE_INVALID, }; + GstH265ProfileTierLevel tmp_ptl; + guint i, len = 0; + guint chroma_format_idc, bit_depth_luma, bit_depth_chroma; + + g_return_val_if_fail (sps != NULL, GST_H265_PROFILE_INVALID); + + tmp_ptl = sps->profile_tier_level; + chroma_format_idc = sps->chroma_format_idc; + bit_depth_luma = sps->bit_depth_luma_minus8 + 8; + bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8; + + gst_h265_profile_tier_level_get_profiles (&sps->profile_tier_level, profiles, + &len); + + for (i = 0; i < len && i < G_N_ELEMENTS (profiles); i++) { + switch (profiles[i]) { + case GST_H265_PROFILE_INVALID: + break; + case GST_H265_PROFILE_MAIN: + case GST_H265_PROFILE_MAIN_STILL_PICTURE: + /* A.3.2 or A.3.5 */ + if (chroma_format_idc == 1 + && bit_depth_luma == 8 && bit_depth_chroma == 8) + return profiles[i]; + break; + case GST_H265_PROFILE_MAIN_10: + /* A.3.3 */ + if (chroma_format_idc == 1 + && bit_depth_luma >= 8 && bit_depth_luma <= 10 + && bit_depth_chroma >= 8 && bit_depth_chroma <= 10) + return profiles[i]; + break; + default: + return profiles[i]; + } + } + + /* Invalid profile: */ + /* Set the conformance indicators based on chroma_format_idc / bit_depth */ + switch (chroma_format_idc) { + case 0: + tmp_ptl.max_monochrome_constraint_flag = 1; + tmp_ptl.max_420chroma_constraint_flag = 1; + tmp_ptl.max_422chroma_constraint_flag = 1; + break; + + case 1: + tmp_ptl.max_monochrome_constraint_flag = 0; + tmp_ptl.max_420chroma_constraint_flag = 1; + tmp_ptl.max_422chroma_constraint_flag = 1; + break; + + case 2: + tmp_ptl.max_monochrome_constraint_flag = 0; + tmp_ptl.max_420chroma_constraint_flag = 0; + tmp_ptl.max_422chroma_constraint_flag = 1; + break; + + case 3: + tmp_ptl.max_monochrome_constraint_flag = 0; + tmp_ptl.max_420chroma_constraint_flag = 0; + tmp_ptl.max_422chroma_constraint_flag = 0; + break; + + default: + g_assert_not_reached (); + break; + } + + tmp_ptl.max_8bit_constraint_flag = 1; + tmp_ptl.max_10bit_constraint_flag = 1; + tmp_ptl.max_12bit_constraint_flag = 1; + tmp_ptl.max_14bit_constraint_flag = 1; + + if (bit_depth_luma > 8 || bit_depth_chroma > 8) + tmp_ptl.max_8bit_constraint_flag = 0; + + if (bit_depth_luma > 10 || bit_depth_chroma > 10) + tmp_ptl.max_10bit_constraint_flag = 0; + + if (bit_depth_luma > 12 || bit_depth_chroma > 12) + tmp_ptl.max_12bit_constraint_flag = 0; + + if (tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_HIGH_THROUGHPUT + || tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING + || tmp_ptl.profile_idc == + GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION + || tmp_ptl.profile_idc == + GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION + || tmp_ptl.profile_compatibility_flag[5] + || tmp_ptl.profile_compatibility_flag[9] + || tmp_ptl.profile_compatibility_flag[10] + || tmp_ptl.profile_compatibility_flag[11]) { + if (bit_depth_luma > 14 || bit_depth_chroma > 14) + tmp_ptl.max_14bit_constraint_flag = 0; + } else { + tmp_ptl.max_14bit_constraint_flag = 0; + } + + /* first profile of the synthetic ptl */ + return gst_h265_profile_tier_level_get_profile (&tmp_ptl); +} + +/* *INDENT-OFF* */ +static void +gst_clear_h265_decoder_config_record_nalu_array ( + GstH265DecoderConfigRecordNalUnitArray * array) +{ + if (!array) + return; + + if (array->nalu) + g_array_unref (array->nalu); +} +/* *INDENT-ON* */ + +/** + * gst_h265_decoder_config_record_free: + * @config: (nullable): a #GstH265DecoderConfigRecord data + * + * Free @config data + * + * Since: 1.24 + */ +void +gst_h265_decoder_config_record_free (GstH265DecoderConfigRecord * config) +{ + if (!config) + return; + + if (config->nalu_array) + g_array_unref (config->nalu_array); + + g_free (config); +} + +static GstH265DecoderConfigRecord * +gst_h265_decoder_config_record_new (void) +{ + GstH265DecoderConfigRecord *config; + + config = g_new0 (GstH265DecoderConfigRecord, 1); + config->nalu_array = g_array_new (FALSE, + FALSE, sizeof (GstH265DecoderConfigRecordNalUnitArray)); + g_array_set_clear_func (config->nalu_array, + (GDestroyNotify) gst_clear_h265_decoder_config_record_nalu_array); + + return config; +} + +/** + * gst_h265_parser_parse_decoder_config_record: + * @parser: a #GstH265Parser + * @data: the data to parse + * @size: the size of @data + * @config: (out): parsed #GstH265DecoderConfigRecord data + * + * Parses HEVCDecoderConfigurationRecord data and fill into @config. + * The caller must free @config via gst_h265_decoder_config_record_free() + * + * This method does not parse VPS, SPS and PPS and therefore the caller needs to + * parse each NAL unit via appropriate parsing method. + * + * Returns: a #GstH265ParserResult + * + * Since: 1.24 + */ +GstH265ParserResult +gst_h265_parser_parse_decoder_config_record (GstH265Parser * parser, + const guint8 * data, gsize size, GstH265DecoderConfigRecord ** config) +{ + GstH265DecoderConfigRecord *ret; + GstBitReader br; + GstH265ParserResult result = GST_H265_PARSER_OK; + guint i; + guint8 num_of_arrays; + + g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (data != NULL, GST_H265_PARSER_ERROR); + g_return_val_if_fail (config != NULL, GST_H265_PARSER_ERROR); + +#define READ_CONFIG_UINT8(val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint8 (&br, (guint8 *) &val, nbits)) { \ + GST_WARNING ("Failed to read " G_STRINGIFY (val)); \ + result = GST_H265_PARSER_ERROR; \ + goto error; \ + } \ +} G_STMT_END; + +#define READ_CONFIG_UINT16(val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint16 (&br, &val, nbits)) { \ + GST_WARNING ("Failed to read " G_STRINGIFY (val)); \ + result = GST_H265_PARSER_ERROR; \ + goto error; \ + } \ +} G_STMT_END; + +#define SKIP_CONFIG_BITS(nbits) G_STMT_START { \ + if (!gst_bit_reader_skip (&br, nbits)) { \ + GST_WARNING ("Failed to skip %d bits", nbits); \ + result = GST_H265_PARSER_ERROR; \ + goto error; \ + } \ +} G_STMT_END; + + *config = NULL; + + if (size < 23) { + GST_WARNING ("Too small size hvcC"); + return GST_H265_PARSER_ERROR; + } + + gst_bit_reader_init (&br, data, size); + + ret = gst_h265_decoder_config_record_new (); + + READ_CONFIG_UINT8 (ret->configuration_version, 8); + if (ret->configuration_version != 1) { + GST_WARNING ("Wrong configurationVersion %d", ret->configuration_version); + /* Must be 1 but allows 0 for backward compatibility. + * See commit + * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/63fee31a3f95021fa0bb2429c723f5356c9b3c4b */ + if (ret->configuration_version != 0) { + result = GST_H265_PARSER_ERROR; + goto error; + } + } + + READ_CONFIG_UINT8 (ret->general_profile_space, 2); + READ_CONFIG_UINT8 (ret->general_tier_flag, 1); + READ_CONFIG_UINT8 (ret->general_profile_idc, 5); + + for (i = 0; i < 32; i++) + READ_CONFIG_UINT8 (ret->general_profile_compatibility_flags[i], 1); + + /* 7.3.3 Profile, tier and level syntax */ + READ_CONFIG_UINT8 (ret->general_progressive_source_flag, 1); + READ_CONFIG_UINT8 (ret->general_interlaced_source_flag, 1); + READ_CONFIG_UINT8 (ret->general_non_packed_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_frame_only_constraint_flag, 1); + + if (ret->general_profile_idc == 4 || + ret->general_profile_compatibility_flags[4] || + ret->general_profile_idc == 5 || + ret->general_profile_compatibility_flags[5] || + ret->general_profile_idc == 6 || + ret->general_profile_compatibility_flags[6] || + ret->general_profile_idc == 7 || + ret->general_profile_compatibility_flags[7] || + ret->general_profile_idc == 8 || + ret->general_profile_compatibility_flags[8] || + ret->general_profile_idc == 9 || + ret->general_profile_compatibility_flags[9] || + ret->general_profile_idc == 10 || + ret->general_profile_compatibility_flags[10] || + ret->general_profile_idc == 11 || + ret->general_profile_compatibility_flags[11]) { + READ_CONFIG_UINT8 (ret->general_max_12bit_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_max_10bit_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_max_8bit_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_max_422chroma_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_max_420chroma_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_max_monochrome_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_intra_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_one_picture_only_constraint_flag, 1); + READ_CONFIG_UINT8 (ret->general_lower_bit_rate_constraint_flag, 1); + + if (ret->general_profile_idc == 5 || + ret->general_profile_compatibility_flags[5] || + ret->general_profile_idc == 9 || + ret->general_profile_compatibility_flags[9] || + ret->general_profile_idc == 10 || + ret->general_profile_compatibility_flags[10] || + ret->general_profile_idc == 11 || + ret->general_profile_compatibility_flags[11]) { + READ_CONFIG_UINT8 (ret->general_max_14bit_constraint_flag, 1); + SKIP_CONFIG_BITS (33); + } else { + SKIP_CONFIG_BITS (34); + } + } else if (ret->general_profile_idc == 2 || + ret->general_profile_compatibility_flags[2]) { + SKIP_CONFIG_BITS (7); + READ_CONFIG_UINT8 (ret->general_one_picture_only_constraint_flag, 1); + + SKIP_CONFIG_BITS (35); + } else { + SKIP_CONFIG_BITS (43); + } + + if (ret->general_profile_idc == 1 || + ret->general_profile_compatibility_flags[1] || + ret->general_profile_idc == 2 || + ret->general_profile_compatibility_flags[2] || + ret->general_profile_idc == 3 || + ret->general_profile_compatibility_flags[3] || + ret->general_profile_idc == 4 || + ret->general_profile_compatibility_flags[4] || + ret->general_profile_idc == 5 || + ret->general_profile_compatibility_flags[5] || + ret->general_profile_idc == 9 || + ret->general_profile_compatibility_flags[9] || + ret->general_profile_idc == 11 || + ret->general_profile_compatibility_flags[11]) { + READ_CONFIG_UINT8 (ret->general_inbld_flag, 1); + } else { + SKIP_CONFIG_BITS (1); + } + + g_assert (gst_bit_reader_get_pos (&br) == 12 * 8); + + READ_CONFIG_UINT8 (ret->general_level_idc, 8); + + SKIP_CONFIG_BITS (4); + READ_CONFIG_UINT16 (ret->min_spatial_segmentation_idc, 12); + + SKIP_CONFIG_BITS (6); + READ_CONFIG_UINT8 (ret->parallelism_type, 2); + + SKIP_CONFIG_BITS (6); + READ_CONFIG_UINT8 (ret->chroma_format_idc, 2); + + SKIP_CONFIG_BITS (5); + READ_CONFIG_UINT8 (ret->bit_depth_luma_minus8, 3); + + SKIP_CONFIG_BITS (5); + READ_CONFIG_UINT8 (ret->bit_depth_chroma_minus8, 3); + + READ_CONFIG_UINT16 (ret->avg_frame_rate, 16); + + READ_CONFIG_UINT8 (ret->constant_frame_rate, 2); + READ_CONFIG_UINT8 (ret->num_temporal_layers, 3); + READ_CONFIG_UINT8 (ret->temporal_id_nested, 1); + READ_CONFIG_UINT8 (ret->length_size_minus_one, 2); + if (ret->length_size_minus_one == 2) { + /* "length_size_minus_one + 1" should be 1, 2, or 4 */ + GST_WARNING ("Wrong nal-length-size"); + } + + READ_CONFIG_UINT8 (num_of_arrays, 8); + + g_assert (gst_bit_reader_get_pos (&br) == 23 * 8); + for (i = 0; i < num_of_arrays; i++) { + GstH265DecoderConfigRecordNalUnitArray array; + guint8 nalu_type; + GstH265NalUnit nalu; + guint16 num_nalu, j; + guint offset; + + READ_CONFIG_UINT8 (array.array_completeness, 1); + SKIP_CONFIG_BITS (1); + READ_CONFIG_UINT8 (nalu_type, 6); + array.nal_unit_type = nalu_type; + + READ_CONFIG_UINT16 (num_nalu, 16); + + offset = gst_bit_reader_get_pos (&br) / 8; + array.nalu = g_array_sized_new (FALSE, FALSE, sizeof (GstH265NalUnit), + num_nalu); + for (j = 0; j < num_nalu; j++) { + result = gst_h265_parser_identify_nalu_hevc (parser, data, offset, size, + 2, &nalu); + if (result != GST_H265_PARSER_OK) { + g_array_unref (array.nalu); + /* Ignores parsing error if this is the last nalu and not an essential + * nalu for decoding */ + if (i + 1 == num_of_arrays && j + 1 == num_nalu && + nalu_type != GST_H265_NAL_VPS && nalu_type != GST_H265_NAL_SPS && + nalu_type != GST_H265_NAL_PPS) { + GST_WARNING ("Couldn't parse the last nalu, type %d at array %d / %d", + nalu_type, i, j); + goto out; + } + goto error; + } + + g_array_append_val (array.nalu, nalu); + offset = nalu.offset + nalu.size; + } + + g_array_append_val (ret->nalu_array, array); + + if (i != num_of_arrays - 1 && !gst_bit_reader_set_pos (&br, offset * 8)) { + GST_WARNING ("Not enough byte for NAL reading"); + result = GST_H265_PARSER_ERROR; + goto error; + } + } + +out: + *config = ret; + return GST_H265_PARSER_OK; + +error: + { + gst_h265_decoder_config_record_free (ret); + return result; + } + +#undef READ_CONFIG_UINT8 +#undef READ_CONFIG_UINT16 +#undef SKIP_CONFIG_BITS +} + +/** + * gst_h265_parser_link_slice_hdr: + * @parser: a #GstH265Parser + * @slice: The #GstH265SliceHdr to fill. + * + * Link SPS and PPS of @parser to @slice. @slice must be valid and parsed + * already by @parser or other #GstH265Parser + * + * Returns: a #GstH265ParserResult + * + * Since: 1.28 + */ +GstH265ParserResult +gst_h265_parser_link_slice_hdr (GstH265Parser * parser, GstH265SliceHdr * slice) +{ + GstH265ParserResult ret; + GstH265PPS *pps; + + g_return_val_if_fail (parser, GST_H265_PARSER_ERROR); + g_return_val_if_fail (slice, GST_H265_PARSER_ERROR); + g_return_val_if_fail (slice->pps_id < GST_H265_MAX_PPS_COUNT, + GST_H265_PARSER_ERROR); + + pps = gst_h265_parser_get_pps (parser, slice->pps_id); + if (!pps) { + GST_WARNING + ("couldn't find associated picture parameter set with id: %d", + slice->pps_id); + return GST_H265_PARSER_BROKEN_LINK; + } + + ret = gst_h265_parser_fill_pps (parser, pps); + if (ret != GST_H265_PARSER_OK) { + GST_WARNING ("couldn't fill pps id: %d", slice->pps_id); + return ret; + } + + slice->pps = pps; + + return GST_H265_PARSER_OK; +} diff --git a/src/h265_parser/gsth265parser.h b/src/h265_parser/gsth265parser.h new file mode 100644 index 0000000..16c47f8 --- /dev/null +++ b/src/h265_parser/gsth265parser.h @@ -0,0 +1,2329 @@ +/* Gstreamer H.265 bitstream parser + * Copyright (C) 2013 Intel Corporation + * Copyright (C) 2013 Sreerenj Balachandran + * + * Contact: Sreerenj Balachandran + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_H265_PARSER_H__ +#define __GST_H265_PARSER_H__ + +#ifndef GST_USE_UNSTABLE_API +#warning "The H.265 parsing library is unstable API and may change in future." +#warning "You can define GST_USE_UNSTABLE_API to avoid this warning." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GST_H265_MAX_SUB_LAYERS 8 +#define GST_H265_MAX_VPS_COUNT 16 +#define GST_H265_MAX_SPS_COUNT 16 +#define GST_H265_MAX_PPS_COUNT 64 + +#define GST_H265_IS_B_SLICE(slice) ((slice)->type == GST_H265_B_SLICE) +#define GST_H265_IS_P_SLICE(slice) ((slice)->type == GST_H265_P_SLICE) +#define GST_H265_IS_I_SLICE(slice) ((slice)->type == GST_H265_I_SLICE) + +/** + * GST_H265_IS_NAL_TYPE_IDR: + * @nal_type: a #GstH265NalUnitType + * + * Check whether @nal_type is IDR or not + * + * Since: 1.18 + */ +#define GST_H265_IS_NAL_TYPE_IDR(nal_type) \ + ((nal_type) == GST_H265_NAL_SLICE_IDR_W_RADL || (nal_type) == GST_H265_NAL_SLICE_IDR_N_LP) + +/** + * GST_H265_IS_NAL_TYPE_IRAP: + * @nal_type: a #GstH265NalUnitType + * + * Check whether @nal_type is IRAP or not + * + * Since: 1.18 + */ +#define GST_H265_IS_NAL_TYPE_IRAP(nal_type) \ + ((nal_type) >= GST_H265_NAL_SLICE_BLA_W_LP && (nal_type) <= GST_H265_RESERVED_IRAP_NAL_TYPE_MAX) + +/** + * GST_H265_IS_NAL_TYPE_BLA: + * @nal_type: a #GstH265NalUnitType + * + * Check whether @nal_type is BLA or not + * + * Since: 1.18 + */ +#define GST_H265_IS_NAL_TYPE_BLA(nal_type) \ + ((nal_type) >= GST_H265_NAL_SLICE_BLA_W_LP && (nal_type) <= GST_H265_NAL_SLICE_BLA_N_LP) + +/** + * GST_H265_IS_NAL_TYPE_CRA: + * @nal_type: a #GstH265NalUnitType + * + * Check whether @nal_type is CRA or not + * + * Since: 1.18 + */ +#define GST_H265_IS_NAL_TYPE_CRA(nal_type) \ + ((nal_type) == GST_H265_NAL_SLICE_CRA_NUT) + +/** + * GST_H265_IS_NAL_TYPE_RADL: + * @nal_type: a #GstH265NalUnitType + * + * Check whether @nal_type is RADL or not + * + * Since: 1.18 + */ +#define GST_H265_IS_NAL_TYPE_RADL(nal_type) \ + ((nal_type) == GST_H265_NAL_SLICE_RADL_N || (nal_type) == GST_H265_NAL_SLICE_RADL_R) + +/** + * GST_H265_IS_NAL_TYPE_RASL: + * @nal_type: a #GstH265NalUnitType + * + * Check whether @nal_type is RASL or not + * + * Since: 1.18 + */ +#define GST_H265_IS_NAL_TYPE_RASL(nal_type) \ + ((nal_type) == GST_H265_NAL_SLICE_RASL_N || (nal_type) == GST_H265_NAL_SLICE_RASL_R) + +/** + * GstH265Profile: + * @GST_H265_PROFILE_MAIN: Main profile (A.3.2) + * @GST_H265_PROFILE_MAIN_10: Main 10 profile (A.3.3) + * @GST_H265_PROFILE_MAIN_STILL_PICTURE: Main Still Picture profile (A.3.4) + * @GST_H265_PROFILE_MONOCHROME: Monochrome profile (A.3.4) + * @GST_H265_PROFILE_MONOCHROME_12: Monochrome 12-bits profile (A.3.4) + * @GST_H265_PROFILE_MONOCHROME_16: Monochrome 16-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_12: Main profile 12-bits (A.3.4) + * @GST_H265_PROFILE_MAIN_422_10: Main 4:2:2 profile 10-bits (A.3.4) + * @GST_H265_PROFILE_MAIN_422_12: Main 4:2:2 profile 12-bits (A.3.4) + * @GST_H265_PROFILE_MAIN_444: Main 4:4:4 profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_10: Main 4:4:4 10-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_12: Main 4:4:4 12-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_INTRA: Main Intra profile (A.3.4) + * @GST_H265_PROFILE_MAIN_10_INTRA: Main Intra 10-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_12_INTRA: Main Intra 12-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_422_10_INTRA: Main Intra 4:2:2 10-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_422_12_INTRA: Main Intra 4:2:2 12-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_INTRA: Main Intra 4:4:4 profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_10_INTRA: Main Intra 4:4:4 10-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_12_INTRA: Main Intra 4:4:4 12-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_16_INTRA: Main Intra 4:4:4 16-bits profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_STILL_PICTURE: Main 4:4:4 Still Picture profile (A.3.4) + * @GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE: Main 4:4:4 16-bits Still Picture profile (A.3.4) + * @GST_H265_PROFILE_MONOCHROME_10: Monochrome 10-bits profile (A.3.5) (Since: 1.18) + * @GST_H265_PROFILE_HIGH_THROUGHPUT_444: High Throughput 4:4:4 profile (A.3.6) (Since: 1.18) + * @GST_H265_PROFILE_HIGH_THROUGHPUT_444_10: High Throughput 4:4:4 10-bits profile (A.3.6) (Since: 1.18) + * @GST_H265_PROFILE_HIGH_THROUGHPUT_444_14: High Throughput 4:4:4 14-bits profile (A.3.6) (Since: 1.18) + * @GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA: High Throughput 4:4:4 16-bits Intra profile (A.3.6) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_MAIN: Screen-Extended Main profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10: Screen-Extended Main 10-bits profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444: Screen-Extended Main 4:4:4 profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10: Screen-Extended Main 4:4:4 10-bits profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444: Screen-Extended High Throughput 4:4:4 profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10: Screen-Extended High Throughput 4:4:4 10-bits profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14: Screen-Extended High Throughput 4:4:4 14-bits profile (A.3.7) (Since: 1.18) + * @GST_H265_PROFILE_MULTIVIEW_MAIN: Multiview Main profile (G.11.1) (Since: 1.18) + * @GST_H265_PROFILE_SCALABLE_MAIN: Scalable Main profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_SCALABLE_MAIN_10: Scalable Main 10-bits profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_SCALABLE_MONOCHROME: Scalable Monochrome profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_SCALABLE_MONOCHROME_12: Scalable Monochrome 12-bits profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_SCALABLE_MONOCHROME_16: Scalable Monochrome 16-bits profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_SCALABLE_MAIN_444: Scalable Main 4:4:4 profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_3D_MAIN: 3D Main 4:4:4 profile (I.11.1) (Since: 1.18) + * + * H.265 Profiles. + * + */ +typedef enum { + GST_H265_PROFILE_INVALID = -1, + GST_H265_PROFILE_MAIN = 1, + GST_H265_PROFILE_MAIN_10 = 2, + GST_H265_PROFILE_MAIN_STILL_PICTURE = 3, + GST_H265_PROFILE_MONOCHROME, + GST_H265_PROFILE_MONOCHROME_12, + GST_H265_PROFILE_MONOCHROME_16, + GST_H265_PROFILE_MAIN_12, + GST_H265_PROFILE_MAIN_422_10, + GST_H265_PROFILE_MAIN_422_12, + GST_H265_PROFILE_MAIN_444, + GST_H265_PROFILE_MAIN_444_10, + GST_H265_PROFILE_MAIN_444_12, + GST_H265_PROFILE_MAIN_INTRA, + GST_H265_PROFILE_MAIN_10_INTRA, + GST_H265_PROFILE_MAIN_12_INTRA, + GST_H265_PROFILE_MAIN_422_10_INTRA, + GST_H265_PROFILE_MAIN_422_12_INTRA, + GST_H265_PROFILE_MAIN_444_INTRA, + GST_H265_PROFILE_MAIN_444_10_INTRA, + GST_H265_PROFILE_MAIN_444_12_INTRA, + GST_H265_PROFILE_MAIN_444_16_INTRA, + GST_H265_PROFILE_MAIN_444_STILL_PICTURE, + GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE, + GST_H265_PROFILE_MONOCHROME_10, + GST_H265_PROFILE_HIGH_THROUGHPUT_444, + GST_H265_PROFILE_HIGH_THROUGHPUT_444_10, + GST_H265_PROFILE_HIGH_THROUGHPUT_444_14, + GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA, + GST_H265_PROFILE_SCREEN_EXTENDED_MAIN, + GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10, + GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444, + GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10, + GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444, + GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10, + GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14, + GST_H265_PROFILE_MULTIVIEW_MAIN, + GST_H265_PROFILE_SCALABLE_MAIN, + GST_H265_PROFILE_SCALABLE_MAIN_10, + GST_H265_PROFILE_SCALABLE_MONOCHROME, + GST_H265_PROFILE_SCALABLE_MONOCHROME_12, + GST_H265_PROFILE_SCALABLE_MONOCHROME_16, + GST_H265_PROFILE_SCALABLE_MAIN_444, + GST_H265_PROFILE_3D_MAIN, + + /* end of the profiles */ + GST_H265_PROFILE_MAX +} GstH265Profile; + +/** + * GstH265ProfileIDC: + * @GST_H265_PROFILE_IDC_MAIN: Main profile (A.3.2) + * @GST_H265_PROFILE_IDC_MAIN_10: Main 10 profile (A.3.3) + * @GST_H265_PROFILE_IDC_MAIN_STILL_PICTURE: Main Still Picture profile (A.3.4) + * @GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION: Format range extensions profile (A.3.5) + * @GST_H265_PROFILE_IDC_HIGH_THROUGHPUT: High throughput profiles (A.3.6) + * @GST_H265_PROFILE_IDC_MULTIVIEW_MAIN: Multiview Main profiles (G.11.1) (Since: 1.18) + * @GST_H265_PROFILE_IDC_SCALABLE_MAIN: Scalable Main and Scalable Main 10 profile (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING: Screen content coding extensions profiles (A.3.7) + * @GST_H265_PROFILE_IDC_3D_MAIN: 3D Main profile (I.11.1) (Since: 1.18) + * @GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION: Scalable Format range extensions profiles (H.11.1) (Since: 1.18) + * @GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION: High throughput screen content coding extensions profiles (A.3.8) (Since: 1.18) + * + * Valid values for the profile_idc field. This is different from + * #GstH265Profile as an extension idc can be used to encode a whole variety of + * profiles. + * + */ +typedef enum { + GST_H265_PROFILE_IDC_MAIN = 1, + GST_H265_PROFILE_IDC_MAIN_10 = 2, + GST_H265_PROFILE_IDC_MAIN_STILL_PICTURE = 3, + GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION = 4, + GST_H265_PROFILE_IDC_HIGH_THROUGHPUT = 5, + GST_H265_PROFILE_IDC_MULTIVIEW_MAIN = 6, + GST_H265_PROFILE_IDC_SCALABLE_MAIN = 7, + GST_H265_PROFILE_IDC_3D_MAIN = 8, + GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING = 9, + GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION = 10, + GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION = 11, +} GstH265ProfileIDC; + +/** + * GstH265NalUnitType: + * @GST_H265_NAL_SLICE_TRAIL_N: Slice nal of a non-TSA, non-STSA trailing picture + * @GST_H265_NAL_SLICE_TRAIL_R: Slice nal of a non-TSA, non-STSA trailing picture + * @GST_H265_NAL_SLICE_TSA_N: Slice nal of a TSA picture + * @GST_H265_NAL_SLICE_TSA_R: Slice nal of a TSA picture + * @GST_H265_NAL_SLICE_STSA_N: Slice nal of a STSA picture + * @GST_H265_NAL_SLICE_STSA_R: Slice nal of a STSA picture + * @GST_H265_NAL_SLICE_RADL_N: Slice nal of a RADL picture + * @GST_H265_NAL_SLICE_RADL_R: Slice nal of a RADL piicture + * @GST_H265_NAL_SLICE_RASL_N: Slice nal of a RASL picture + * @GST_H265_NAL_SLICE_RASL_R: Slice nal of a RASL picture + * @GST_H265_NAL_SLICE_BLA_W_LP: Slice nal of a BLA picture + * @GST_H265_NAL_SLICE_BLA_W_RADL: Slice nal of a BLA picture + * @GST_H265_NAL_SLICE_BLA_N_LP: Slice nal of a BLA picture + * @GST_H265_NAL_SLICE_IDR_W_RADL: Slice nal of an IDR picture + * @GST_H265_NAL_SLICE_IDR_N_LP: Slice nal of an IDR picture + * @GST_H265_NAL_SLICE_CRA_NUT: Slice nal of a CRA picture + * @GST_H265_NAL_VPS: Video parameter set(VPS) nal unit + * @GST_H265_NAL_SPS: Sequence parameter set (SPS) nal unit + * @GST_H265_NAL_PPS: Picture parameter set (PPS) nal unit + * @GST_H265_NAL_AUD: Access unit (AU) delimiter nal unit + * @GST_H265_NAL_EOS: End of sequence (EOS) nal unit + * @GST_H265_NAL_EOB: End of bitstream (EOB) nal unit + * @GST_H265_NAL_FD: Filler data (FD) nal lunit + * @GST_H265_NAL_PREFIX_SEI: Supplemental enhancement information prefix nal unit + * @GST_H265_NAL_SUFFIX_SEI: Suppliemental enhancement information suffix nal unit + * + * Indicates the type of H265 Nal Units + */ +typedef enum +{ + GST_H265_NAL_SLICE_TRAIL_N = 0, + GST_H265_NAL_SLICE_TRAIL_R = 1, + GST_H265_NAL_SLICE_TSA_N = 2, + GST_H265_NAL_SLICE_TSA_R = 3, + GST_H265_NAL_SLICE_STSA_N = 4, + GST_H265_NAL_SLICE_STSA_R = 5, + GST_H265_NAL_SLICE_RADL_N = 6, + GST_H265_NAL_SLICE_RADL_R = 7, + GST_H265_NAL_SLICE_RASL_N = 8, + GST_H265_NAL_SLICE_RASL_R = 9, + GST_H265_NAL_SLICE_BLA_W_LP = 16, + GST_H265_NAL_SLICE_BLA_W_RADL = 17, + GST_H265_NAL_SLICE_BLA_N_LP = 18, + GST_H265_NAL_SLICE_IDR_W_RADL = 19, + GST_H265_NAL_SLICE_IDR_N_LP = 20, + GST_H265_NAL_SLICE_CRA_NUT = 21, + GST_H265_NAL_VPS = 32, + GST_H265_NAL_SPS = 33, + GST_H265_NAL_PPS = 34, + GST_H265_NAL_AUD = 35, + GST_H265_NAL_EOS = 36, + GST_H265_NAL_EOB = 37, + GST_H265_NAL_FD = 38, + GST_H265_NAL_PREFIX_SEI = 39, + GST_H265_NAL_SUFFIX_SEI = 40 +} GstH265NalUnitType; + +#define GST_H265_RESERVED_NON_IRAP_SUBLAYER_NAL_TYPE_MIN 10 +#define GST_H265_RESERVED_NON_IRAP_SUBLAYER_NAL_TYPE_MAX 15 + +#define GST_H265_RESERVED_IRAP_NAL_TYPE_MIN 22 +#define GST_H265_RESERVED_IRAP_NAL_TYPE_MAX 23 + +#define GST_H265_RESERVED_NON_IRAP_NAL_TYPE_MIN 24 +#define GST_H265_RESERVED_NON_IRAP_NAL_TYPE_MAX 31 + +#define GST_H265_RESERVED_NON_VCL_NAL_TYPE_MIN 41 +#define GST_H265_RESERVED_NON_VCL_NAL_TYPE_MAX 47 + +#define GST_H265_UNSPECIFIED_NON_VCL_NAL_TYPE_MIN 48 +#define GST_H265_UNSPECIFIED_NON_VCL_NAL_TYPE_MAX 63 + +/** + * GstH265ParserResult: + * @GST_H265_PARSER_OK: The parsing succeeded + * @GST_H265_PARSER_BROKEN_DATA: The data to parse is broken + * @GST_H265_PARSER_BROKEN_LINK: The link to structure needed for the parsing couldn't be found + * @GST_H265_PARSER_ERROR: An error accured when parsing + * @GST_H265_PARSER_NO_NAL: No nal found during the parsing + * @GST_H265_PARSER_NO_NAL_END: Start of the nal found, but not the end. + * + * The result of parsing H265 data. + */ +typedef enum +{ + GST_H265_PARSER_OK, + GST_H265_PARSER_BROKEN_DATA, + GST_H265_PARSER_BROKEN_LINK, + GST_H265_PARSER_ERROR, + GST_H265_PARSER_NO_NAL, + GST_H265_PARSER_NO_NAL_END +} GstH265ParserResult; + +/** + * GstH265SEIPayloadType: + * @GST_H265_SEI_BUF_PERIOD: Buffering Period SEI Message + * @GST_H265_SEI_PIC_TIMING: Picture Timing SEI Message + * @GST_H265_SEI_REGISTERED_USER_DATA: Registered user data (D.2.5) + * @GST_H265_SEI_RECOVERY_POINT: Recovery Point SEI Message (D.3.8) + * @GST_H265_SEI_TIME_CODE: Time code SEI message (D.2.27) (Since: 1.16) + * @GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME: Mastering display colour volume information SEI message (D.2.28) (Since: 1.18) + * @GST_H265_SEI_CONTENT_LIGHT_LEVEL: Content light level information SEI message (D.2.35) (Since: 1.18) + * ... + * + * The type of SEI message. + */ +/** + * GST_H265_SEI_USER_DATA_UNREGISTERED + * + * User data unregistered (D.2.7) + * + * Since: 1.24 + */ +typedef enum +{ + GST_H265_SEI_BUF_PERIOD = 0, + GST_H265_SEI_PIC_TIMING = 1, + GST_H265_SEI_REGISTERED_USER_DATA = 4, + GST_H265_SEI_USER_DATA_UNREGISTERED = 5, + GST_H265_SEI_RECOVERY_POINT = 6, + GST_H265_SEI_TIME_CODE = 136, + GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME = 137, + GST_H265_SEI_CONTENT_LIGHT_LEVEL = 144, + /* and more... */ +} GstH265SEIPayloadType; + +/** + * GstH265SEIPicStructType: + * @GST_H265_SEI_PIC_STRUCT_FRAME: Picture is a frame + * @GST_H265_SEI_PIC_STRUCT_TOP_FIELD: Top field of frame + * @GST_H265_SEI_PIC_STRUCT_BOTTOM_FIELD: Botom field of frame + * @GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM: Top bottom field of frame + * @GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP: bottom top field of frame + * @GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: top bottom top field of frame + * @GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: bottom top bottom field of frame + * @GST_H265_SEI_PIC_STRUCT_FRAME_DOUBLING: indicates that the frame should + * be displayed two times consecutively + * @GST_H265_SEI_PIC_STRUCT_FRAME_TRIPLING: indicates that the frame should be + * displayed three times consecutively + * @GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_PREVIOUS_BOTTOM: top field paired with + * previous bottom field in output order + * @GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_PREVIOUS_TOP: bottom field paried with + * previous top field in output order + * @GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_NEXT_BOTTOM: top field paired with next + * bottom field in output order + * @GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_NEXT_TOP: bottom field paired with + * next top field in output order + * + * SEI pic_struct type + */ +typedef enum +{ + GST_H265_SEI_PIC_STRUCT_FRAME = 0, + GST_H265_SEI_PIC_STRUCT_TOP_FIELD = 1, + GST_H265_SEI_PIC_STRUCT_BOTTOM_FIELD = 2, + GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM = 3, + GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP = 4, + GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, + GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, + GST_H265_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, + GST_H265_SEI_PIC_STRUCT_FRAME_TRIPLING = 8, + GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_PREVIOUS_BOTTOM = 9, + GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_PREVIOUS_TOP = 10, + GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_NEXT_BOTTOM = 11, + GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_NEXT_TOP = 12 +} GstH265SEIPicStructType; + +/** + * GstH265SliceType: + * + * Type of Picture slice + */ + +typedef enum +{ + GST_H265_B_SLICE = 0, + GST_H265_P_SLICE = 1, + GST_H265_I_SLICE = 2 +} GstH265SliceType; + +typedef enum +{ + GST_H265_QUANT_MATIX_4X4 = 0, + GST_H265_QUANT_MATIX_8X8 = 1, + GST_H265_QUANT_MATIX_16X16 = 2, + GST_H265_QUANT_MATIX_32X32 = 3 +} GstH265QuantMatrixSize; + + +/** + * GstH265Level: + * @GST_H265_LEVEL_L1: Level 1 + * @GST_H265_LEVEL_L2: Level 2 + * @GST_H265_LEVEL_L2_1: Level 2.1 + * @GST_H265_LEVEL_L3: Level 3 + * @GST_H265_LEVEL_L3_1: Level 3.1 + * @GST_H265_LEVEL_L4: Level 4 + * @GST_H265_LEVEL_L4_1: Level 4.1 + * @GST_H265_LEVEL_L5: Level 5 + * @GST_H265_LEVEL_L5_1: Level 5.1 + * @GST_H265_LEVEL_L5_2: Level 5.2 + * @GST_H265_LEVEL_L6: Level 6 + * @GST_H265_LEVEL_L6_1: Level 6.1 + * @GST_H265_LEVEL_L6_2: Level 6.2 + * + * H.265 level + * + * Since: 1.24 + */ +typedef enum +{ + GST_H265_LEVEL_L1 = 30, + GST_H265_LEVEL_L2 = 60, + GST_H265_LEVEL_L2_1 = 63, + GST_H265_LEVEL_L3 = 90, + GST_H265_LEVEL_L3_1 = 93, + GST_H265_LEVEL_L4 = 120, + GST_H265_LEVEL_L4_1 = 123, + GST_H265_LEVEL_L5 = 150, + GST_H265_LEVEL_L5_1 = 153, + GST_H265_LEVEL_L5_2 = 156, + GST_H265_LEVEL_L6 = 180, + GST_H265_LEVEL_L6_1 = 183, + GST_H265_LEVEL_L6_2 = 186, +} GstH265Level; + +typedef struct _GstH265Parser GstH265Parser; + +typedef struct _GstH265NalUnit GstH265NalUnit; + +typedef struct _GstH265VPS GstH265VPS; +typedef struct _GstH265SPS GstH265SPS; +typedef struct _GstH265SPSEXT GstH265SPSEXT; +typedef struct _GstH265PPS GstH265PPS; +typedef struct _GstH265ProfileTierLevel GstH265ProfileTierLevel; +typedef struct _GstH265SubLayerHRDParams GstH265SubLayerHRDParams; +typedef struct _GstH265HRDParams GstH265HRDParams; +typedef struct _GstH265VUIParams GstH265VUIParams; +typedef struct _GstH265SPSExtensionParams GstH265SPSExtensionParams; +typedef struct _GstH265SPSSccExtensionParams GstH265SPSSccExtensionParams; +typedef struct _GstH265PPSExtensionParams GstH265PPSExtensionParams; +typedef struct _GstH265PPSSccExtensionParams GstH265PPSSccExtensionParams; + +typedef struct _GstH265ScalingList GstH265ScalingList; +typedef struct _GstH265RefPicListModification GstH265RefPicListModification; +typedef struct _GstH265PredWeightTable GstH265PredWeightTable; +typedef struct _GstH265ShortTermRefPicSet GstH265ShortTermRefPicSet; +typedef struct _GstH265ShortTermRefPicSetExt GstH265ShortTermRefPicSetExt; +typedef struct _GstH265SliceHdr GstH265SliceHdr; + +typedef struct _GstH265PicTiming GstH265PicTiming; +typedef struct _GstH265RegisteredUserData GstH265RegisteredUserData; +typedef struct _GstH265UserDataUnregistered GstH265UserDataUnregistered; +typedef struct _GstH265BufferingPeriod GstH265BufferingPeriod; +typedef struct _GstH265RecoveryPoint GstH265RecoveryPoint; +typedef struct _GstH265TimeCode GstH265TimeCode; +typedef struct _GstH265MasteringDisplayColourVolume GstH265MasteringDisplayColourVolume; +typedef struct _GstH265ContentLightLevel GstH265ContentLightLevel; +typedef struct _GstH265SEIMessage GstH265SEIMessage; +typedef struct _GstH265DecoderConfigRecordNalUnitArray GstH265DecoderConfigRecordNalUnitArray; +typedef struct _GstH265DecoderConfigRecord GstH265DecoderConfigRecord; + +/** + * GstH265NalUnit: + * @type: A #GstH265NalUnitType + * @layer_id: A nal unit layer id + * @temporal_id_plus1: A nal unit temporal identifier + * @size: The size of the nal unit starting from @offset + * @offset: The offset of the actual start of the nal unit + * @sc_offset:The offset of the start code of the nal unit + * @valid: If the nal unit is valid, which mean it has + * already been parsed + * @data: The data from which the Nalu has been parsed + * + * Structure defining the Nal unit headers + */ +struct _GstH265NalUnit +{ + guint8 type; + guint8 layer_id; + guint8 temporal_id_plus1; + + /* calculated values */ + guint size; + guint offset; + guint sc_offset; + gboolean valid; + + guint8 *data; + guint8 header_bytes; +}; + +/** + * GstH265ProfileTierLevel: + * @profile_space: specifies the context for the interpretation of + * general_profile_idc and general_profile_combatibility_flag + * @tier_flag: the tier context for the interpretation of general_level_idc + * @profile_idc: profile id + * @profile_compatibility_flag: compatibility flags + * @progressive_source_flag: flag to indicate the type of stream + * @interlaced_source_flag: flag to indicate the type of stream + * @non_packed_constraint_flag: indicate the presence of frame packing + * arrangement sei message + * @frame_only_constraint_flag: recognize the field_seq_flag + * @max_12bit_constraint_flag: used to define profile extensions, see Annex A + * @max_10bit_constraint_flag: used to define profile extensions, see Annex A + * @max_8bit_constraint_flag: used to define profile extensions, see Annex A + * @max_422chroma_constraint_flag: used to define profile extensions, see Annex A + * @max_420chroma_constraint_flag: used to define profile extensions, see Annex A + * @max_monochrome_constraint_flag: used to define profile extensions, see Annex A + * @intra_constraint_flag: used to define profile extensions, see Annex A + * @one_picture_only_constraint_flag: used to define profile extensions, see Annex A + * @lower_bit_rate_constraint_flag: used to define profile extensions, see Annex A + * @max_14bit_constraint_flag: used to define profile extensions, see Annex A + * @level idc: indicate the level which the CVS confirms + * @sub_layer_profile_present_flag: sublayer profile presence ind + * @sub_layer_level_present_flag:sublayer level presence indicator. + * @sub_layer_profile_space: profile space for sublayers + * @sub_layer_tier_flag: tier flags for sublayers. + * @sub_layer_profile_idc: conformant profile indicator for sublayers. + * @sub_layer_profile_compatibility_flag[6][32]: compatibility flags for sublayers + * @sub_layer_progressive_source_flag:progressive stream indicator for sublayer + * @sub_layer_interlaced_source_flag: interlaced stream indicator for sublayer + * @sub_layer_non_packed_constraint_flag: indicate the presence of + * frame packing arrangement sei message with in sublayers + * @sub_layer_frame_only_constraint_flag:recognize the sublayer + * specific field_seq_flag + * @sub_layer_level_idc:indicate the sublayer specific level + * + * Define ProfileTierLevel parameters + */ +struct _GstH265ProfileTierLevel { + guint8 profile_space; + guint8 tier_flag; + guint8 profile_idc; + + guint8 profile_compatibility_flag[32]; + + guint8 progressive_source_flag; + guint8 interlaced_source_flag; + guint8 non_packed_constraint_flag; + guint8 frame_only_constraint_flag; + + guint8 max_12bit_constraint_flag; + guint8 max_10bit_constraint_flag; + guint8 max_8bit_constraint_flag; + guint8 max_422chroma_constraint_flag; + guint8 max_420chroma_constraint_flag; + guint8 max_monochrome_constraint_flag; + guint8 intra_constraint_flag; + guint8 one_picture_only_constraint_flag; + guint8 lower_bit_rate_constraint_flag; + guint8 max_14bit_constraint_flag; + + guint8 level_idc; + + guint8 sub_layer_profile_present_flag[6]; + guint8 sub_layer_level_present_flag[6]; + + guint8 sub_layer_profile_space[6]; + guint8 sub_layer_tier_flag[6]; + guint8 sub_layer_profile_idc[6]; + guint8 sub_layer_profile_compatibility_flag[6][32]; + guint8 sub_layer_progressive_source_flag[6]; + guint8 sub_layer_interlaced_source_flag[6]; + guint8 sub_layer_non_packed_constraint_flag[6]; + guint8 sub_layer_frame_only_constraint_flag[6]; + guint8 sub_layer_level_idc[6]; +}; + +/** + * GstH265SubLayerHRDParams: + * @bit_rate_value_minus1:togeter with bit_rate_scale, it specifies + * the maximum input bitrate when the CPB operates at the access + * unit level + * @cpb_size_value_minus1: is used together with cpb_size_scale to + * specify the CPB size when the CPB operates at the access unit + * level + * @cpb_size_du_value_minus1: is used together with cpb_size_du_scale + * to specify the CPB size when the CPB operates at sub-picture + * level + * @bit_rate_du_value_minus1: together with bit_rate_scale, it + * specifies the maximum input bit rate when the CPB operates at the + * sub-picture level + * @cbr_flag: indicate whether HSS operates in intermittent bit rate + * mode or constant bit rate mode. + * + * Defines the Sublayer HRD parameters + */ +struct _GstH265SubLayerHRDParams +{ + guint32 bit_rate_value_minus1[32]; + guint32 cpb_size_value_minus1[32]; + + guint32 cpb_size_du_value_minus1[32]; + guint32 bit_rate_du_value_minus1[32]; + + guint8 cbr_flag[32]; +}; + +/** + * GstH265HRDParams: + * @nal_hrd_parameters_present_flag: indicate the presence of NAL HRD parameters + * @vcl_hrd_parameters_present_flag: indicate the presence of VCL HRD parameters + * @sub_pic_hrd_params_present_flag: indicate the presence of sub_pic_hrd_params + * @tick_divisor_minus2: is used to specify the clock sub-tick + * @du_cpb_removal_delay_increment_length_minus1: specifies the length, + * in bits, of the nal_initial_cpb_removal_delay + * @sub_pic_cpb_params_in_pic_timing_sei_flag: specifies the length, in bits, of + * the cpb_delay_offset and the au_cpb_removal_delay_minus1 syntax elements. + * @dpb_output_delay_du_length_minu1: specifies the length, in bits, of the + * dpb_delay_offset and the pic_dpb_output_delay syntax elements + * @bit_rate_scale: maximum input bitrate + * @cpb_size_scale: CPB size when operates in access unit level + * @cpb_size_du_scale: CPB size when operates in sub-picture level + * @initial_cpb_removal_delay_length_minus1: specifies the length, in bits, of the + * nal_initial_cpb_removal_delay, nal_initial_cpb_removal_offset, + * vcl_initial_cpb_removal_delay and vcl_initial_cpb_removal_offset. + * @au_cpb_removal_delay_length_minus1: specifies the length, in bits, of the + * cpb_delay_offset and the au_cpb_removal_delay_minus1 syntax elements + * @dpb_output_delay_length_minus1: specifies the length, in bits, of the + * dpb_delay_offset and the pic_dpb_output_delay syntax elements + * @fixed_pic_rate_general_flag: flag to indicate the presence of constraint + * on the temporal distance between the HRD output times of consecutive + * pictures in output order + * @fixed_pic_rate_within_cvs_flag: same as fixed_pic_rate_general_flag + * @elemental_duration_in_tc_minus1: temporal distance in clock ticks + * @low_delay_hrd_flag: specifies the HRD operational mode + * @cpb_cnt_minus1:specifies the number of alternative CPS specifications. + * @sublayer_hrd_params: Sublayer HRD parameters. + * + * Defines the HRD parameters + */ +struct _GstH265HRDParams +{ + guint8 nal_hrd_parameters_present_flag; + guint8 vcl_hrd_parameters_present_flag; + guint8 sub_pic_hrd_params_present_flag; + + guint8 tick_divisor_minus2; + guint8 du_cpb_removal_delay_increment_length_minus1; + guint8 sub_pic_cpb_params_in_pic_timing_sei_flag; + guint8 dpb_output_delay_du_length_minus1; + + guint8 bit_rate_scale; + guint8 cpb_size_scale; + guint8 cpb_size_du_scale; + + guint8 initial_cpb_removal_delay_length_minus1; + guint8 au_cpb_removal_delay_length_minus1; + guint8 dpb_output_delay_length_minus1; + + guint8 fixed_pic_rate_general_flag [7]; + guint8 fixed_pic_rate_within_cvs_flag [7]; + guint16 elemental_duration_in_tc_minus1 [7]; + guint8 low_delay_hrd_flag [7]; + guint8 cpb_cnt_minus1[7]; + + GstH265SubLayerHRDParams sublayer_hrd_params[7]; +}; + +/** + * GstH265VPS: + * @id: vps id + * @base_layer_internal_flag and @base_layer_available_flag: + * specify availability of base layer + * @max_layers_minus1: should be zero, but can be other values in future + * @max_sub_layers_minus1:specifies the maximum number of temporal sub-layers + * @temporal_id_nesting_flag: specifies whether inter prediction is + * additionally restricted + * @profile_tier_level: ProfileTierLevel info + * @sub_layer_ordering_info_present_flag: indicates the presence of + * vps_max_dec_pic_buffering_minus1, vps_max_num_reorder_pics and + * vps_max_latency_increase_plus1 + * @max_dec_pic_buffering_minus1: specifies the maximum required size + * of the decoded picture buffer + * @max_num_reorder_pics: indicates the maximum allowed number of + * pictures that can precede any picture in the CVS in decoding + * order + * @max_latency_increase_plus1: is used to compute the value of + * VpsMaxLatencyPictures + * @max_layer_id: specifies the maximum allowed value of nuh_layer_id + * @num_layer_sets_minus1: specifies the number of layer sets + * @layer_id_included_flag: specifies whether a nuh_layer_id included + * in the layer identifier list + * @timing_info_present_flag: indicate the presence of + * num_units_in_tick, time_scale, poc_proportional_to_timing_flag + * and num_hrd_parameters + * @num_units_in_tick: number of time units in a tick + * @time_scale: number of time units that pass in one second + * @poc_proportional_to_timing_flag: indicate whether the picture + * order count is proportional to output timin + * @num_ticks_poc_diff_one_minus1: specifies the number of clock ticks + * corresponding to a difference of picture order count values equal + * to 1 + * @num_hrd_parameters: number of hrd_parameters present + * @hrd_layer_set_idx: index to the list of layer hrd params. + * @hrd_params: the GstH265HRDParams list + * + * Defines the VPS parameters + */ +struct _GstH265VPS { + guint8 id; + + guint8 base_layer_internal_flag; + guint8 base_layer_available_flag; + + guint8 max_layers_minus1; + guint8 max_sub_layers_minus1; + guint8 temporal_id_nesting_flag; + + GstH265ProfileTierLevel profile_tier_level; + + guint8 sub_layer_ordering_info_present_flag; + guint8 max_dec_pic_buffering_minus1[GST_H265_MAX_SUB_LAYERS]; + guint8 max_num_reorder_pics[GST_H265_MAX_SUB_LAYERS]; + guint32 max_latency_increase_plus1[GST_H265_MAX_SUB_LAYERS]; + + guint8 max_layer_id; + guint16 num_layer_sets_minus1; + + guint8 timing_info_present_flag; + guint32 num_units_in_tick; + guint32 time_scale; + guint8 poc_proportional_to_timing_flag; + guint32 num_ticks_poc_diff_one_minus1; + + guint16 num_hrd_parameters; + + /* FIXME: following HRD related info should be an array */ + guint16 hrd_layer_set_idx; + guint8 cprms_present_flag; + GstH265HRDParams hrd_params; + + guint8 vps_extension; + + gboolean valid; +}; +/** + * GstH265ShortTermRefPicSet: + * @inter_ref_pic_set_prediction_flag: %TRUE specifies that the stRpsIdx-th candidate + * short-term RPS is predicted from another candidate short-term RPS + * @delta_idx_minus1: plus 1 specifies the difference between the value of source and + * candidate short term RPS. + * @delta_rps_sign: delta_rps_sign and abs_delta_rps_minus1 together specify + * the value of the variable deltaRps. + * @abs_delta_rps_minus1: delta_rps_sign and abs_delta_rps_minus1 together specify + * the value of the variable deltaRps + * @NumDeltaPocs: sum of @NumNegativePics and @NumPositivePics. + * @NumNegativePics: Derived value depending on inter_ref_pic_set_prediction_flag. + * If inter_ref_pic_set_prediction_flag is equal to 0, this specifies + * the number of entries in the stRpsIdx-th candidate + * short-term RPS that have poc values less than the poc of the current picture. + * @NumPositivePics: Derived value depending on inter_ref_pic_set_prediction_flag. + * If inter_ref_pic_set_prediction_flag is equal to 0, this specifies + * the number of entires in the stRpsIdx-th candidate + * short-term RPS that have poc values greater than the poc value of the current picture. + * @UsedByCurrPicS0: Derived value depending on inter_ref_pic_set_prediction_flag. + * If inter_ref_pic_set_prediction_flag is equal to 0, + * equal to zero specifies that the i-th entry in the stRpsIdx-th + * candidate short-term RPS that has poc value less than of the current picture + * is not used for reference by the current picture + * @UsedByCurrPicS1: Derived value depending on inter_ref_pic_set_prediction_flag. + * If inter_ref_pic_set_prediction_flag is equal to 0, + * equal to zero specifies that the i-th entry in the current + * candidate short-term RPS that has poc value greater than that of the current picture + * is not used for reference by the current picture. + * @DeltaPocS0: Derived value depending on inter_ref_pic_set_prediction_flag. + * See 7.4.8 Short-term reference picture set semantics + * @DeltaPocS1: Derived value depending on inter_ref_pic_set_prediction_flag. + * See 7.4.8 Short-term reference picture set semantics + * @NumDeltaPocsOfRefRpsIdx: The value of NumDeltaPocs[RefRpsIdx]. + * If inter_ref_pic_set_prediction_flag is equal to 0, + * this value should be ignored (Since: 1.18) + * + * Defines the #GstH265ShortTermRefPicSet params + */ +struct _GstH265ShortTermRefPicSet +{ + guint8 inter_ref_pic_set_prediction_flag; + guint8 delta_idx_minus1; + guint8 delta_rps_sign; + guint16 abs_delta_rps_minus1; + + /* calculated values */ + guint8 NumDeltaPocs; + guint8 NumNegativePics; + guint8 NumPositivePics; + guint8 UsedByCurrPicS0[16]; + guint8 UsedByCurrPicS1[16]; + gint32 DeltaPocS0[16]; + gint32 DeltaPocS1[16]; + guint8 NumDeltaPocsOfRefRpsIdx; +}; + +/** + * GstH265ShortTermRefPicSetExt: + * @use_delta_flag: Bit j equals to 1 specifies that the j-th entry in the source candidate + * short-term RPS is included in this candidate short-term RPS. + * @used_by_curr_pic: Bit j specifies if short-term RPS j is used by the current picture. + * @delta_poc_s0_minus1: Specifies the negative picture order count delta for the i-th + * entry in the short-term RPS. See details in section 7.4.8 "Short-term reference + * picture set semantics" of the specification. + * @delta_poc_s1_minus1: Specifies the positive picture order count delta for the i-th + * entry in the short-term RPS. See details in section 7.4.8 "Short-term reference + * picture set semantics" of the specification. + * + * Defines the extended #GstH265ShortTermRefPicSetExt params + * + * Since: 1.28 + */ +struct _GstH265ShortTermRefPicSetExt +{ + guint8 use_delta_flag[16]; + guint8 used_by_curr_pic_flag[16]; + guint32 delta_poc_s0_minus1[16]; + guint32 delta_poc_s1_minus1[16]; +}; + +/** + * GstH265VUIParams: + * @parsed: %TRUE indicate that VUI parameters have been parsed (Since: 1.22) + * @aspect_ratio_info_present_flag: %TRUE specifies that aspect_ratio_idc is present. + * %FALSE specifies that aspect_ratio_idc is not present + * @aspect_ratio_idc specifies the value of the sample aspect ratio of the luma samples + * @sar_width indicates the horizontal size of the sample aspect ratio + * @sar_height indicates the vertical size of the sample aspect ratio + * @overscan_info_present_flag: %TRUE overscan_appropriate_flag is present %FALSE otherwise + * @overscan_appropriate_flag: %TRUE indicates that the cropped decoded pictures + * output are suitable for display using overscan. %FALSE the cropped decoded pictures + * output contain visually important information + * @video_signal_type_present_flag: %TRUE specifies that video_format, video_full_range_flag and + * colour_description_present_flag are present. + * @video_format: indicates the representation of the picture + * @video_full_range_flag: indicates the black level and range of the luma and chroma signals + * @colour_description_present_flag: %TRUE specifies that colour_primaries, + * transfer_characteristics and matrix_coefficients are present + * @colour_primaries: indicates the chromaticity coordinates of the source primaries + * @transfer_characteristics: indicates the opto-electronic transfer characteristic + * @matrix_coefficients: describes the matrix coefficients used in deriving luma and chroma signals + * @chroma_loc_info_present_flag: %TRUE specifies that chroma_sample_loc_type_top_field and + * chroma_sample_loc_type_bottom_field are present, %FALSE otherwise + * @chroma_sample_loc_type_top_field: specify the location of chroma for top field + * @chroma_sample_loc_type_bottom_field specify the location of chroma for bottom field + * @neutral_chroma_indication_flag: %TRUE indicate that the value of chroma samples is equla + * to 1<<(BitDepthchrom-1). + * @field_seq_flag: %TRUE indicate field and %FALSE indicate frame + * @frame_field_info_present_flag: %TRUE indicate picture timing SEI messages are present for every + * picture and include the pic_struct, source_scan_type, and duplicate_flag syntax elements. + * @default_display_window_flag: %TRUE indicate that the default display window parameters present + * def_disp_win_left_offset:left offset of display rect + * def_disp_win_right_offset: right offset of display rect + * def_disp_win_top_offset: top offset of display rect + * def_disp_win_bottom_offset: bottom offset of display rect + * @timing_info_present_flag: %TRUE specifies that num_units_in_tick, + * time_scale and fixed_frame_rate_flag are present in the bitstream + * @num_units_in_tick: is the number of time units of a clock operating at the frequency time_scale Hz + * @time_scale: is the number of time units that pass in one second + * @poc_proportional_to_timing_flag: %TRUE indicates that the picture order count value for each picture + * in the CVS that is not the first picture in the CVS, in decoding order, is proportional to the output + * time of the picture relative to the output time of the first picture in the CVS. + * @num_ticks_poc_diff_one_minus1: plus 1 specifies the number of clock ticks corresponding to a + * difference of picture order count values equal to 1 + * @hrd_parameters_present_flag: %TRUE if hrd parameters present in the bitstream + * @bitstream_restriction_flag: %TRUE specifies that the following coded video sequence bitstream restriction + * parameters are present + * @tiles_fixed_structure_flag: %TRUE indicates that each PPS that is active in the CVS has the same value + * of the syntax elements num_tile_columns_minus1, num_tile_rows_minus1, uniform_spacing_flag, + * column_width_minus1, row_height_minus1 and loop_filter_across_tiles_enabled_flag, when present + * @motion_vectors_over_pic_boundaries_flag: %FALSE indicates that no sample outside the + * picture boundaries and no sample at a fractional sample position, %TRUE indicates that one or more + * samples outside picture boundaries may be used in inter prediction + * @restricted_ref_pic_list_flag: %TRUE indicates that all P and B slices (when present) that belong to + * the same picture have an identical reference picture list 0, and that all B slices (when present) + * that belong to the same picture have an identical reference picture list 1 + * @min_spatial_segmentation_idc: when not equal to 0, establishes a bound on the maximum possible size + * of distinct coded spatial segmentation regions in the pictures of the CVS + * @max_bytes_per_pic_denom: indicates a number of bytes not exceeded by the sum of the sizes of + * the VCL NAL units associated with any coded picture in the coded video sequence. + * @max_bits_per_min_cu_denom: indicates an upper bound for the number of coded bits of coding_unit + * data for any coding block in any picture of the CVS + * @log2_max_mv_length_horizontal: indicate the maximum absolute value of a decoded horizontal + * motion vector component + * @log2_max_mv_length_vertical: indicate the maximum absolute value of a decoded vertical + * motion vector component + * + * The structure representing the VUI parameters. + */ +struct _GstH265VUIParams +{ + /** + * _GstH265VUIParams.parsed: + * + * %TRUE indicate that VUI parameters have been parsed. + * + * Since: 1.22 + */ + gboolean parsed; + guint8 aspect_ratio_info_present_flag; + guint8 aspect_ratio_idc; + /* if aspect_ratio_idc == 255 */ + guint16 sar_width; + guint16 sar_height; + + guint8 overscan_info_present_flag; + /* if overscan_info_present_flag */ + guint8 overscan_appropriate_flag; + + guint8 video_signal_type_present_flag; + guint8 video_format; + guint8 video_full_range_flag; + guint8 colour_description_present_flag; + guint8 colour_primaries; + guint8 transfer_characteristics; + guint8 matrix_coefficients; + + guint8 chroma_loc_info_present_flag; + guint8 chroma_sample_loc_type_top_field; + guint8 chroma_sample_loc_type_bottom_field; + + guint8 neutral_chroma_indication_flag; + guint8 field_seq_flag; + guint8 frame_field_info_present_flag; + guint8 default_display_window_flag; + guint32 def_disp_win_left_offset; + guint32 def_disp_win_right_offset; + guint32 def_disp_win_top_offset; + guint32 def_disp_win_bottom_offset; + + guint8 timing_info_present_flag; + /* if timing_info_present_flag */ + guint32 num_units_in_tick; + guint32 time_scale; + guint8 poc_proportional_to_timing_flag; + /* if poc_proportional_to_timing_flag */ + guint32 num_ticks_poc_diff_one_minus1; + guint8 hrd_parameters_present_flag; + /*if hrd_parameters_present_flat */ + GstH265HRDParams hrd_params; + + guint8 bitstream_restriction_flag; + /* if bitstream_restriction_flag */ + guint8 tiles_fixed_structure_flag; + guint8 motion_vectors_over_pic_boundaries_flag; + guint8 restricted_ref_pic_lists_flag; + guint16 min_spatial_segmentation_idc; + guint8 max_bytes_per_pic_denom; + guint8 max_bits_per_min_cu_denom; + guint8 log2_max_mv_length_horizontal; + guint8 log2_max_mv_length_vertical; + + /* calculated values */ + guint par_n; + guint par_d; +}; + +/** + * GstH265SPSExtensionParams: + * @transform_skip_rotation_enabled_flag: %TRUE specifies that a rotation is applied to + * the residual data block from intra 4X4 blocks coded using a transform skip operation. + * @transform_skip_context_enabled_flag: %TRUE specifies tspecifies that a particular + * context is used for the parsing of the sig_coeff_flag for transform blocks with a skipped + * transform. + * @implicit_residual_dpcm_enabled_flag: %TRUE specifies that the residual modification process + * for blocks using a transform bypass may be used for intra blocks in the CVS + * @explicit_residual_dpcm_enabled_flag: %TRUE specifies that the residual modification process + * for blocks using a transform bypass may be used for inter blocks in the CVS + * @extended_precision_processing_flag: %TRUE specifies that an extended dynamic range is used + * for coefficient parsing and inverse transform processing + * @intra_smoothing_disabled_flag: %TRUE specifies that the filtering process of neighbouring + * samples is unconditionally disabled for intra prediction + * @high_precision_offsets_enabled_flag: %TRUE specifies that weighted prediction offset values + * are signalled using a bit-depth-dependent precision. + * @persistent_rice_adaptation_enabled_flag: %TRUE specifies that the Rice parameter derivation + * for the binarization of coeff_abs_level_remaining[] is initialized at the start of each + * sub-block using mode dependent statistics accumulated from previous sub-blocks. + * @cabac_bypass_alignment_enabled_flag: %TRUE specifies that a context-based adaptive binary + * arithmetic coding (CABAC) alignment process is used prior to bypass decoding of the syntax + * elements coeff_sign_flag[] and coeff_abs_level_remaining[] + * + * Defines the GstH265SPSExtensionParams + */ +struct _GstH265SPSExtensionParams { + guint8 transform_skip_rotation_enabled_flag; + guint8 transform_skip_context_enabled_flag; + guint8 implicit_rdpcm_enabled_flag; + guint8 explicit_rdpcm_enabled_flag; + guint8 extended_precision_processing_flag; + guint8 intra_smoothing_disabled_flag; + guint8 high_precision_offsets_enabled_flag; + guint8 persistent_rice_adaptation_enabled_flag; + guint8 cabac_bypass_alignment_enabled_flag; +}; + +/** + * GstH265SPSSccExtensionParams: + * @sps_curr_pic_ref_enabled_flag: equal to 1 specifies that a picture in the CVS may be + * included in a reference picture list of a slice of the picture itself. + * @palette_mode_enabled_flag: equal to 1 specifies that the decoding process for palette mode + * may be used for intra blocks. Equal to 0 specifies that the decoding process for palette + * mode is not applied. + * @palette_max_size: specifies the maximum allowed palette size. + * @delta_palette_max_predictor_size: specifies the difference between the maximum allowed + * palette predictor size and the maximum allowed palette size. + * @sps_palette_predictor_initializers_present_flag: equal to 1 specifies that the sequence + * palette predictors are initialized using the sps_palette_predictor_initializer specified + * in clause 7.3.2.2.3. + * @sps_num_palette_predictor_initializer_minus1: plus 1 specifies the number of entries in + * the sequence palette predictor initializer. + * @sps_palette_predictor_initializer: specifies the value of the comp-th component of the + * i-th palette entry in the SPS that is used to initialize the array PredictorPaletteEntries. + * @motion_vector_resolution_control_idc: controls the presence and inference of the + * use_integer_mv_flag that specifies the resolution of motion vectors for inter prediction. + * @intra_boundary_filtering_disabled_flag: equal to 1 specifies that the intra boundary + * filtering process is unconditionally disabled for intra prediction. + * Defines the _GstH265SPSSccExtensionParams + * + * Since: 1.18 + */ +struct _GstH265SPSSccExtensionParams { + guint8 sps_curr_pic_ref_enabled_flag; + guint8 palette_mode_enabled_flag; + guint8 palette_max_size; + guint8 delta_palette_max_predictor_size; + guint8 sps_palette_predictor_initializers_present_flag; + guint8 sps_num_palette_predictor_initializer_minus1; + guint32 sps_palette_predictor_initializer[3][128]; + guint8 motion_vector_resolution_control_idc; + guint8 intra_boundary_filtering_disabled_flag; +}; + +/** + * GstH265PPSExtensionParams: + * @log2_max_transform_skip_block_size_minus2: plus 2 specifies the maximum transform block size for which + * transform_skip_flag may be present in coded pictures referring to the PPS. + * @cross_component_prediction_enabled_flag: equal to 1 specifies that log2_res_scale_abs_plus1 and + * res_scale_sign_flag may be present in the transform unit syntax for pictures referring to the PPS. + * @chroma_qp_offset_list_enabled_flag: equal to 1 specifies that the cu_chroma_qp_offset_flag may be + * present in the transform unit syntax. + * @diff_cu_chroma_qp_offset_depth: specifies the difference between the luma coding tree block size and + * the minimum luma coding block size of coding units that convey cu_chroma_qp_offset_flag. + * @chroma_qp_offset_list_len_minus1: plus 1 specifies the number of cb_qp_offset_list[] and + * cr_qp_offset_list[] syntax elements that are present in the PPS. + * @cb_qp_offset_list: specify offsets used in the derivation of qp cb. + * @cr_qp_offset_list: specify offsets used in the derivation of qp cr. + * @log2_sao_offset_scale_luma: the base 2 logarithm of the scaling parameter that is used to scale sample + * adaptive offset (SAO) offset values for luma samples. + * @log2_sao_offset_scale_chroma: the base 2 logarithm of the scaling parameter that is used to scale SAO + * offset values for chroma samples. + * + * Defines the GstH265SPSExtensionParams + */ +struct _GstH265PPSExtensionParams { + guint32 log2_max_transform_skip_block_size_minus2; + guint8 cross_component_prediction_enabled_flag; + guint8 chroma_qp_offset_list_enabled_flag; + guint8 diff_cu_chroma_qp_offset_depth; + guint8 chroma_qp_offset_list_len_minus1; + gint8 cb_qp_offset_list[6]; + gint8 cr_qp_offset_list[6]; + guint8 log2_sao_offset_scale_luma; + guint8 log2_sao_offset_scale_chroma; +}; + +/** + * GstH265PPSSccExtensionParams: + * @pps_curr_pic_ref_enabled_flag: equal to 1 specifies that a picture referring to the PPS may + * be included in a reference picture list of a slice of the picture itself. + * @residual_adaptive_colour_transform_enabled_flag: equal to 1 specifies that an adaptive + * colour transform may be applied to the residual in the decoding process. + * @pps_slice_act_qp_offsets_present_flag: equal to 1 specifies that slice_act_y_qp_offset, + * slice_act_cb_qp_offset, slice_act_cr_qp_offset are present in the slice header. + * @pps_act_y_qp_offset_plus5 @pps_act_cb_qp_offset_plus5 @pps_act_cr_qp_offset_plus3: + * are used to determine the offsets that are applied to the quantization parameter values + * qp derived in clause 8.6.2 for the luma, Cb and Cr components, respectively, when + * tu_residual_act_flag[ xTbY ][ yTbY ] is equal to 1. + * @pps_palette_predictor_initializers_present_flag: equal to 1 specifies that the palette + * predictor initializers used for the pictures referring to the PPS are derived based on + * the palette predictor initializers specified by the PPS. + * @pps_num_palette_predictor_initializer: specifies the number of entries in the picture + * palette predictor initializer. + * @monochrome_palette_flag: equal to 1 specifies that the pictures that refer to this PPS + * are monochrome. Equal to 0 specifies that the pictures that refer to this PPS have + * multiple components. + * @luma_bit_depth_entry_minus8: plus 8 specifies the bit depth of the luma component of the + * entries of the palette predictor initializer. + * @chroma_bit_depth_entry_minus8: plus 8 specifies the bit depth of the chroma components of + * the entries of the palette predictor initializer. + * @pps_palette_predictor_initializer: specifies the value of the comp-th component of the + * i-th palette entry in the PPS that is used to initialize the array PredictorPaletteEntries. + * Defines the _GstH265PPSSccExtensionParams + * + * Since: 1.18 + */ +struct _GstH265PPSSccExtensionParams { + guint8 pps_curr_pic_ref_enabled_flag; + guint8 residual_adaptive_colour_transform_enabled_flag; + guint8 pps_slice_act_qp_offsets_present_flag; + guint8 pps_act_y_qp_offset_plus5; + guint8 pps_act_cb_qp_offset_plus5; + guint8 pps_act_cr_qp_offset_plus3; + guint8 pps_palette_predictor_initializers_present_flag; + guint8 pps_num_palette_predictor_initializer; + guint8 monochrome_palette_flag; + guint8 luma_bit_depth_entry_minus8; + guint32 chroma_bit_depth_entry_minus8; + guint32 pps_palette_predictor_initializer[3][128]; +}; + +/** + * GstH265ScalingList: + * @scaling_list_dc_coef_minus8_16x16: this plus 8 specifies the DC + * Coefficient values for 16x16 scaling list + * @scaling_list_dc_coef_minus8_32x32: this plus 8 specifies the DC + * Coefficient values for 32x32 scaling list + * @scaling_lists_4x4: 4x4 scaling list + * @scaling_lists_8x8: 8x8 scaling list + * @scaling_lists_16x16: 16x16 scaling list + * @guint8 scaling_lists_32x32: 32x32 scaling list + * + * Defines the GstH265ScalingList + */ +struct _GstH265ScalingList { + + gint16 scaling_list_dc_coef_minus8_16x16[6]; + gint16 scaling_list_dc_coef_minus8_32x32[2]; + + guint8 scaling_lists_4x4 [6][16]; + guint8 scaling_lists_8x8 [6][64]; + guint8 scaling_lists_16x16 [6][64]; + guint8 scaling_lists_32x32 [2][64]; +}; + +/** + * GstH265SPS: + * @id: The ID of the sequence parameter set + * @profile_idc: indicate the profile to which the coded video sequence conforms + * + * H265 Sequence Parameter Set (SPS) + */ +struct _GstH265SPS +{ + guint8 id; + + /** + * _GstH265SPS.vps_id: + * + * The ID of the VPS. This is used to store the ID until the VPS is + * parsed in case its placed after the SPS. + * Since: 1.22 + */ + guint8 vps_id; + GstH265VPS *vps; + + guint8 max_sub_layers_minus1; + guint8 temporal_id_nesting_flag; + + GstH265ProfileTierLevel profile_tier_level; + + guint8 chroma_format_idc; + guint8 separate_colour_plane_flag; + guint16 pic_width_in_luma_samples; + guint16 pic_height_in_luma_samples; + + guint8 conformance_window_flag; + /* if conformance_window_flag */ + guint32 conf_win_left_offset; + guint32 conf_win_right_offset; + guint32 conf_win_top_offset; + guint32 conf_win_bottom_offset; + + guint8 bit_depth_luma_minus8; + guint8 bit_depth_chroma_minus8; + guint8 log2_max_pic_order_cnt_lsb_minus4; + + guint8 sub_layer_ordering_info_present_flag; + guint8 max_dec_pic_buffering_minus1[GST_H265_MAX_SUB_LAYERS]; + guint8 max_num_reorder_pics[GST_H265_MAX_SUB_LAYERS]; + guint8 max_latency_increase_plus1[GST_H265_MAX_SUB_LAYERS]; + + guint8 log2_min_luma_coding_block_size_minus3; + guint8 log2_diff_max_min_luma_coding_block_size; + guint8 log2_min_transform_block_size_minus2; + guint8 log2_diff_max_min_transform_block_size; + guint8 max_transform_hierarchy_depth_inter; + guint8 max_transform_hierarchy_depth_intra; + + guint8 scaling_list_enabled_flag; + /* if scaling_list_enabled_flag */ + guint8 scaling_list_data_present_flag; + + GstH265ScalingList scaling_list; + + guint8 amp_enabled_flag; + guint8 sample_adaptive_offset_enabled_flag; + guint8 pcm_enabled_flag; + /* if pcm_enabled_flag */ + guint8 pcm_sample_bit_depth_luma_minus1; + guint8 pcm_sample_bit_depth_chroma_minus1; + guint8 log2_min_pcm_luma_coding_block_size_minus3; + guint8 log2_diff_max_min_pcm_luma_coding_block_size; + guint8 pcm_loop_filter_disabled_flag; + + guint8 num_short_term_ref_pic_sets; + GstH265ShortTermRefPicSet short_term_ref_pic_set[65]; + + guint8 long_term_ref_pics_present_flag; + /* if long_term_ref_pics_present_flag */ + guint8 num_long_term_ref_pics_sps; + guint16 lt_ref_pic_poc_lsb_sps[32]; + guint8 used_by_curr_pic_lt_sps_flag[32]; + + guint8 temporal_mvp_enabled_flag; + guint8 strong_intra_smoothing_enabled_flag; + guint8 vui_parameters_present_flag; + + /* if vui_parameters_present_flag */ + GstH265VUIParams vui_params; + + guint8 sps_extension_flag; + + /* if sps_extension_present_flag */ + guint8 sps_range_extension_flag; + guint8 sps_multilayer_extension_flag; + guint8 sps_3d_extension_flag; + guint8 sps_scc_extension_flag; + guint8 sps_extension_4bits; + + /* if sps_range_extension_flag */ + /** + * _GstH265SPS.sps_extension_params: + * + * Since: 1.22 + */ + GstH265SPSExtensionParams sps_extension_params; + /* if sps_scc_extension_flag */ + GstH265SPSSccExtensionParams sps_scc_extension_params; + + /* calculated values */ + guint8 chroma_array_type; + gint width, height; + gint crop_rect_width, crop_rect_height; + gint crop_rect_x, crop_rect_y; + gint fps_num, fps_den; + gboolean valid; +}; + +/** + * GstH265SPSEXT: + * + * H265 Sequence Parameter Set extension + * + * Since: 1.28 + */ +struct _GstH265SPSEXT { + GstH265ShortTermRefPicSetExt short_term_ref_pic_set_ext[65]; +}; + +/** + * GstH265PPS: + * + * H265 Picture Parameter Set + */ +struct _GstH265PPS +{ + guint id; + + /** + * _GstH265PPS.sps_id: + * + * The ID of the SPS. This is used to store the ID until the SPS is + * parsed in case its placed after the PPS. + * + * Since: 1.22 + */ + guint sps_id; + GstH265SPS *sps; + + guint8 dependent_slice_segments_enabled_flag; + guint8 output_flag_present_flag; + guint8 num_extra_slice_header_bits; + guint8 sign_data_hiding_enabled_flag; + guint8 cabac_init_present_flag; + guint8 num_ref_idx_l0_default_active_minus1; + guint8 num_ref_idx_l1_default_active_minus1; + gint8 init_qp_minus26; + guint8 constrained_intra_pred_flag; + guint8 transform_skip_enabled_flag; + guint8 cu_qp_delta_enabled_flag; + /*if cu_qp_delta_enabled_flag */ + guint8 diff_cu_qp_delta_depth; + + gint8 cb_qp_offset; + gint8 cr_qp_offset; + guint8 slice_chroma_qp_offsets_present_flag; + guint8 weighted_pred_flag; + guint8 weighted_bipred_flag; + guint8 transquant_bypass_enabled_flag; + guint8 tiles_enabled_flag; + guint8 entropy_coding_sync_enabled_flag; + + guint8 num_tile_columns_minus1; + guint8 num_tile_rows_minus1; + guint8 uniform_spacing_flag; + guint32 column_width_minus1[20]; + guint32 row_height_minus1[22]; + guint8 loop_filter_across_tiles_enabled_flag; + + guint8 loop_filter_across_slices_enabled_flag; + guint8 deblocking_filter_control_present_flag; + guint8 deblocking_filter_override_enabled_flag; + guint8 deblocking_filter_disabled_flag; + gint8 beta_offset_div2; + gint8 tc_offset_div2; + + guint8 scaling_list_data_present_flag; + + GstH265ScalingList scaling_list; + + guint8 lists_modification_present_flag; + guint8 log2_parallel_merge_level_minus2; + guint8 slice_segment_header_extension_present_flag; + + guint8 pps_extension_flag; + + /* if pps_extension_flag*/ + guint8 pps_range_extension_flag; + guint8 pps_multilayer_extension_flag; + guint8 pps_3d_extension_flag; + guint8 pps_scc_extension_flag; + guint8 pps_extension_4bits; + + /* if pps_range_extension_flag*/ + GstH265PPSExtensionParams pps_extension_params; + /* if pps_scc_extension_flag*/ + GstH265PPSSccExtensionParams pps_scc_extension_params; + + /* calculated values */ + guint32 PicWidthInCtbsY; + guint32 PicHeightInCtbsY; + gboolean valid; +}; + +struct _GstH265RefPicListModification +{ + guint8 ref_pic_list_modification_flag_l0; + guint32 list_entry_l0[15]; + guint8 ref_pic_list_modification_flag_l1; + guint32 list_entry_l1[15]; +}; + +struct _GstH265PredWeightTable +{ + guint8 luma_log2_weight_denom; + gint8 delta_chroma_log2_weight_denom; + + guint8 luma_weight_l0_flag[15]; + guint8 chroma_weight_l0_flag[15]; + gint8 delta_luma_weight_l0[15]; + gint8 luma_offset_l0[15]; + gint8 delta_chroma_weight_l0 [15][2]; + gint16 delta_chroma_offset_l0 [15][2]; + + guint8 luma_weight_l1_flag[15]; + guint8 chroma_weight_l1_flag[15]; + gint8 delta_luma_weight_l1[15]; + gint8 luma_offset_l1[15]; + gint8 delta_chroma_weight_l1[15][2]; + gint16 delta_chroma_offset_l1[15][2]; +}; + +/** + * GstH265SliceHdr: + * @first_slice_segment_in_pic_flag: equal to 1 if this slice segment is + * the first slice segment of the picture in decoding order + * @no_output_of_prior_pics_flag: affects the output of previously-decoded pictures + * in the decoded picture buffer after the decoding of an IDR or a BLA picture + * that is not the first picture in the bitstream as specified in Annex C + * @pps: a #GstH265PPS + * @dependent_slice_segment_flag: equal to 1 if the value of each slice segment header + * syntax element that is not present is inferred to be equal to the value of corresponding + * slice segment header syntax element in the slice header. + * @segment_address: the address of the first CTB in the slice segment + * @type: slice type (B, P, or I) + * @pic_output_flag: affects the decoded picture output and removal processes + * as specified in Annex C. + * @colour_plane_id: specifies the colour plane associated with the current slice RBSP + * when separate_colour_plane_flag is equal to 1 + * @pic_order_cnt_lsb: the picture order count modulo MaxPicOrderCntLsb for the current picture + * @short_term_ref_pic_set_sps_flag: equal to 1 specifies that the short-term RPS + * of the current picture is derived based on the active SPS. + * @short_term_ref_pic_sets: a #GstH265ShortTermRefPicSet structure + * @short_term_ref_pic_set_idx: the index of st_ref_pic_set syntax structure + * that is used for derivation of the short-term RPS of the current picture. + * @num_long_term_sps: the number of entries in the long-term RPS of current picture + * that are derived based on the syntax in active SPS. + * @num_long_term_pics: the number of entries in the long-term RPS of the current picture + * that are directly signalled in the slice header. + * @lt_idx_sps: the index of candidate long-term reference pictures + * specified in the active SPS. + * @poc_lsb_lt: the value of the picture order count modulo MaxPicOrderCntLsb + * of the each entry in the long-term RPS of the current picture. + * @used_by_curr_pic_lt_flag: equal to 0 if the entry in the long-term RPS + * of the current picture is not used for reference by the current picture. + * @delta_poc_msb_present_flag: equal to 1 if i-th delta_poc_msb_cycle_lt[] is present. + * @delta_poc_msb_cycle_lt: used to determine the value of the most significant bits + * of the picture order count value of the i-th entry in the long-term RPS of the current picture. + * @temporal_mvp_enabled_flag: whether temporal motion vector predictors can be used for inter prediction. + * @sao_luma_flag: equal to 1 if SAO is enabled for the luma component in the current slice. + * @sao_chroma_flag: equal to 1 if SAO is enabled for the chroma component in the current slice. + * @num_ref_idx_active_override_flag: equal to 1 specifies that the syntax elements + * num_ref_idx_l0_active_minus1 and num_ref_idx_l1_active_minus1 are present. + * @num_ref_idx_l0_active_minus1: the maximum reference index for reference picture list 0 + * that may be used to decode the slice. + * @num_ref_idx_l1_active_minus1: the maximum reference index for reference picture list 1 + * that may be used to decode the slice. + * @ref_pic_list_modification: a #GstH265RefPicListModification + * @mvd_l1_zero_flag: equal to 1 if the mvd_coding sytanx structure is not parsed + * @cabac_init_flag: specifies the method for determining the initialization table + * used in the initialization process for context variables. + * @collocated_from_l0_flag: equal to 1 specifies that the collocated picture + * used for temporal motion vector prediction is derived from reference picture list 0. + * @collocated_ref_idx: the reference index of the collocated picture + * used for temporal motion vector prediction. + * @pred_weight_table: a #GstH265PredWeightTable + * @five_minus_max_num_merge_cand: specifies the maximum number of merging motion vector prediction (MVP) + * candidates supported in the slice. + * @use_integer_mv_flag: equal to 1 specifies that the resolution of motion vectors for inter + * prediction in the current slice is integer. (Since: 1.18) + * @qp_delta: specifies the inital value of QPy to be used for the coding blocks in the slice. + * @cb_qp_offset: a difference to be added to the value of pps_cb_qp_offset. + * @cr_qp_offset: a difference to be added to the value of pps_cr_qp_offset. + * @slice_act_y_qp_offset: specify offsets to the quantization parameter values qP derived in + * clause 8.6.2 for luma components. (Since: 1.18) + * @slice_act_cb_qp_offset: specify offsets to the quantization parameter values qP derived in + * clause 8.6.2 for Cb components. (Since: 1.18) + * @slice_act_cr_qp_offset: specify offsets to the quantization parameter values qP derived in + * clause 8.6.2 for Cr components. (Since: 1.18) + * @cu_chroma_qp_offset_enabled_flag: equal to 1 if the cu_chroma_qp_offset_flag + * may be present in the transform unit syntax. (Since: 1.18) + * @deblocking_filter_override_flag: equal to 1 if deblocking paramertes are present in the slice header. + * @deblocking_filter_disabled_flag: equal to 1 specifies that the operation of + * the deblocking filter is not applied for the current slice. + * @beta_offset_div2: deblocking parameter offset for beta divided by 2 for the current slice. + * @tc_offset_div2: deblocking parameter offset for tC divided by 2 for the current slice. + * @loop_filter_across_slices_enabled_flag: equal to 1 specifies that in-loop filtering + * operation may be performed across the left and upper boundaries of the current slice. + * @num_entry_point_offsets: specifies the number of entry_point_offset_minus1 syntax elements + * in the slice header. + * @offset_len_minus1: specifies the length of the entry_point_minus1 syntax elements + * in bits. + * @entry_point_offset_minus1: the entry point offset in bytes. + * @NumPocTotalCurr: calculated NumPocTotalCurr which is used for + * decoding process for reference picture set + * @header_size: the calculated size of the slice_header\() in bits. + * @n_emulation_prevention_bytes: number of emulation prevention bytes (EPB) + * in this slice_header\() + * @short_term_ref_pic_set_size: the calculated size of short_term_ref_pic_set\() + * in bits. (Since: 1.18) + * @long_term_ref_pic_set_size: the calculated size of the branch + * `if( long_term_ref_pics_present_flag )` `inside slice_segment_header()` syntax + * in bits. (Since: 1.22) + */ +struct _GstH265SliceHdr +{ + guint8 first_slice_segment_in_pic_flag; + guint8 no_output_of_prior_pics_flag; + + GstH265PPS *pps; + + guint8 dependent_slice_segment_flag; + guint32 segment_address; + + guint8 type; + + guint8 pic_output_flag; + guint8 colour_plane_id; + guint16 pic_order_cnt_lsb; + + guint8 short_term_ref_pic_set_sps_flag; + GstH265ShortTermRefPicSet short_term_ref_pic_sets; + guint8 short_term_ref_pic_set_idx; + + guint8 num_long_term_sps; + guint8 num_long_term_pics; + guint8 lt_idx_sps[16]; + guint32 poc_lsb_lt[16]; + guint8 used_by_curr_pic_lt_flag[16]; + guint8 delta_poc_msb_present_flag[16]; + guint32 delta_poc_msb_cycle_lt[16]; + + guint8 temporal_mvp_enabled_flag; + guint8 sao_luma_flag; + guint8 sao_chroma_flag; + guint8 num_ref_idx_active_override_flag; + guint8 num_ref_idx_l0_active_minus1; + guint8 num_ref_idx_l1_active_minus1; + + GstH265RefPicListModification ref_pic_list_modification; + + guint8 mvd_l1_zero_flag; + guint8 cabac_init_flag; + guint8 collocated_from_l0_flag; + guint8 collocated_ref_idx; + + GstH265PredWeightTable pred_weight_table; + + guint8 five_minus_max_num_merge_cand; + guint8 use_integer_mv_flag; + + gint8 qp_delta; + gint8 cb_qp_offset; + gint8 cr_qp_offset; + gint8 slice_act_y_qp_offset; + gint8 slice_act_cb_qp_offset; + gint8 slice_act_cr_qp_offset; + + guint8 cu_chroma_qp_offset_enabled_flag; + + guint8 deblocking_filter_override_flag; + guint8 deblocking_filter_disabled_flag; + gint8 beta_offset_div2; + gint8 tc_offset_div2; + + guint8 loop_filter_across_slices_enabled_flag; + + guint32 num_entry_point_offsets; + guint8 offset_len_minus1; + guint32 *entry_point_offset_minus1; + + /* calculated values */ + + gint NumPocTotalCurr; + /* Size of the slice_header() in bits */ + guint header_size; + /* Number of emulation prevention bytes (EPB) in this slice_header() */ + guint n_emulation_prevention_bytes; + + /* Size of short_term_ref_pic_set() after emulation preventation bytes are + * removed, in bits */ + guint short_term_ref_pic_set_size; + + /** + * _GstH265SliceHdr.long_term_ref_pic_set_size: + * + * The calculated size of the branch `if( long_term_ref_pics_present_flag )` + * inside `slice_segment_header()` syntax in bits. + * + * Since: 1.22 + */ + guint long_term_ref_pic_set_size; + + /** + * _GstH265SliceHdr.pps_id: + * + * PPS id + * + * Since: 1.28 + */ + guint pps_id; +}; + +struct _GstH265PicTiming +{ + guint8 pic_struct; + guint8 source_scan_type; + guint8 duplicate_flag; + + guint8 au_cpb_removal_delay_minus1; + guint8 pic_dpb_output_delay; + guint8 pic_dpb_output_du_delay; + guint32 num_decoding_units_minus1; + guint8 du_common_cpb_removal_delay_flag; + guint8 du_common_cpb_removal_delay_increment_minus1; + guint32 *num_nalus_in_du_minus1; + guint8 *du_cpb_removal_delay_increment_minus1; +}; + +struct _GstH265BufferingPeriod +{ + GstH265SPS *sps; + + guint8 irap_cpb_params_present_flag; + guint8 cpb_delay_offset; + guint8 dpb_delay_offset; + guint8 concatenation_flag; + guint8 au_cpb_removal_delay_delta_minus1; + + /* seq->vui_parameters->nal_hrd_parameters_present_flag */ + guint8 nal_initial_cpb_removal_delay[32]; + guint8 nal_initial_cpb_removal_offset[32]; + guint8 nal_initial_alt_cpb_removal_delay[32]; + guint8 nal_initial_alt_cpb_removal_offset [32]; + + /* seq->vui_parameters->vcl_hrd_parameters_present_flag */ + guint8 vcl_initial_cpb_removal_delay[32]; + guint8 vcl_initial_cpb_removal_offset[32]; + guint8 vcl_initial_alt_cpb_removal_delay[32]; + guint8 vcl_initial_alt_cpb_removal_offset[32]; +}; + +struct _GstH265RecoveryPoint +{ + gint32 recovery_poc_cnt; + guint8 exact_match_flag; + guint8 broken_link_flag; +}; + +/** + * GstH265RegisteredUserData: + * The User data registered by Rec. ITU-T T.35 SEI message. + * @country_code: an itu_t_t35_country_code. + * @country_code_extension: an itu_t_t35_country_code_extension_byte. + * Should be ignored when @country_code is not 0xff + * @data: the data of itu_t_t35_payload_byte + * excluding @country_code and @country_code_extension + * @size: the size of @data in bytes + * + * Since: 1.18 + */ +struct _GstH265RegisteredUserData +{ + guint8 country_code; + guint8 country_code_extension; + const guint8 *data; + guint size; +}; + +/** + * GstH265UserDataUnregistered: + * @uuid: an uuid_iso_iec_11578. + * @data: the data of user_data_payload_byte + * @size: the size of @data in bytes + * + * The User data unregistered SEI message syntax. + * + * Since: 1.24 + */ +struct _GstH265UserDataUnregistered +{ + guint8 uuid[16]; + const guint8 *data; + guint size; +}; + +/** + * GstH265TimeCode: + * The time code SEI message provides time code information similar to that + * defined by SMPTE ST 12-1 (2014) for field(s) or frame(s) of the current + * picture. + * + * D.2.27 + * + * Since: 1.16 + */ +struct _GstH265TimeCode +{ + guint8 num_clock_ts; + guint8 clock_timestamp_flag[3]; + guint8 units_field_based_flag[3]; + guint8 counting_type[3]; + guint8 full_timestamp_flag[3]; + guint8 discontinuity_flag[3]; + guint8 cnt_dropped_flag[3]; + guint16 n_frames[3]; + guint8 seconds_flag[3]; + guint8 seconds_value[3]; + guint8 minutes_flag[3]; + guint8 minutes_value[3]; + guint8 hours_flag[3]; + guint8 hours_value[3]; + guint8 time_offset_length[3]; + guint32 time_offset_value[3]; +}; + +/** + * GstH265MasteringDisplayColourVolume: + * The colour volume (primaries, white point and luminance range) of display + * defined by SMPTE ST 2086. + * + * D.2.28 + * + * Since: 1.18 + */ +struct _GstH265MasteringDisplayColourVolume +{ + guint16 display_primaries_x[3]; + guint16 display_primaries_y[3]; + guint16 white_point_x; + guint16 white_point_y; + guint32 max_display_mastering_luminance; + guint32 min_display_mastering_luminance; +}; + +/** + * GstH265ContentLightLevel: + * The upper bounds for the nominal target brightness light level + * as specified in CEA-861.3 + * + * D.2.35 + * + * Since: 1.18 + */ +struct _GstH265ContentLightLevel +{ + guint16 max_content_light_level; + guint16 max_pic_average_light_level; +}; + +/** + * GstH265SEIMessage: + * @payloadType: #GstH265SEIPayloadType + * @payload: union of all possible SEI message data types + * + * Constains information about SEI message. The content depends on the + * @payloadType. + */ +struct _GstH265SEIMessage +{ + GstH265SEIPayloadType payloadType; + + union { + GstH265BufferingPeriod buffering_period; + GstH265PicTiming pic_timing; + GstH265RegisteredUserData registered_user_data; + GstH265RecoveryPoint recovery_point; + GstH265TimeCode time_code; + GstH265MasteringDisplayColourVolume mastering_display_colour_volume; + GstH265ContentLightLevel content_light_level; + + /** + * GstH265SEIMessage.user_data_unregistered: + * + * User Data Unregistered + * + * Since: 1.24 + */ + GstH265UserDataUnregistered user_data_unregistered; + /* ... could implement more */ + } payload; +}; + +/** + * GstH265DecoderConfigRecordNalUnitArray: + * + * Contains NAL Unit array data as defined in ISO/IEC 14496-15 + * + * Since: 1.24 + */ +struct _GstH265DecoderConfigRecordNalUnitArray +{ + /** + * GstH265DecoderConfigRecordNalUnitArray.array_completeness: + * + * 1: all NAL units of the given type are in this array and none + * are in the stream. + * 0: additional NAL units of the indicated type may be in the stream + */ + guint8 array_completeness; + + /** + * GstH265DecoderConfigRecordNalUnitArray.nal_unit_type: + * + * Indicates the type of the NAL units in the following array. + * Shall be VPS, SPS, PPS, prefix SEI or suffix SEI + */ + GstH265NalUnitType nal_unit_type; + + /** + * GstH265DecoderConfigRecordNalUnitArray.nalu: + * + * Array of identified #GstH265NalUnit + */ + GArray *nalu; +}; + +/** + * GstH265DecoderConfigRecord: + * + * Contains HEVCDecoderConfigurationRecord data as defined in ISO/IEC 14496-15 + * + * Since: 1.24 + */ +struct _GstH265DecoderConfigRecord +{ + /** + * GstH265DecoderConfigRecord.configuration_version: + * + * Indicates configurationVersion, must be 1 + */ + guint8 configuration_version; + + /** + * GstH265DecoderConfigRecord.general_profile_space: + * + * general profile space + */ + guint8 general_profile_space; + + /** + * GstH265DecoderConfigRecord.general_tier_flag: + * + * general tier flag + */ + guint8 general_tier_flag; + + /** + * GstH265DecoderConfigRecord.general_profile_idc: + * + * general profile indiction + */ + guint8 general_profile_idc; + + /** + * GstH265DecoderConfigRecord.general_profile_compatibility_flags: + * + * general profile compatibility flags + */ + guint8 general_profile_compatibility_flags[32]; + + /** + * GstH265DecoderConfigRecord.general_progressive_source_flag: + * + * general_progressive_source_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_progressive_source_flag; + + /** + * GstH265DecoderConfigRecord.general_interlaced_source_flag: + * + * general_interlaced_source_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_interlaced_source_flag; + + /** + * GstH265DecoderConfigRecord.general_non_packed_constraint_flag: + * + * general_non_packed_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_non_packed_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_frame_only_constraint_flag: + * + * general_frame_only_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_frame_only_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_12bit_constraint_flag: + * + * general_max_12bit_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_12bit_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_10bit_constraint_flag: + * + * general_max_10bit_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_10bit_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_8bit_constraint_flag: + * + * general_max_8bit_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_8bit_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_422chroma_constraint_flag: + * + * general_max_422chroma_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_422chroma_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_420chroma_constraint_flag: + * + * general_max_420chroma_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_420chroma_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_monochrome_constraint_flag: + * + * general_max_monochrome_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_monochrome_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_intra_constraint_flag: + * + * general_intra_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_intra_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_one_picture_only_constraint_flag: + * + * general_one_picture_only_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_one_picture_only_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_lower_bit_rate_constraint_flag: + * + * general_lower_bit_rate_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_lower_bit_rate_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_max_14bit_constraint_flag: + * + * general_max_14bit_constraint_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_max_14bit_constraint_flag; + + /** + * GstH265DecoderConfigRecord.general_inbld_flag: + * + * general_inbld_flag parsed from + * HEVCDecoderConfigurationRecord.general_constraint_indicator_flags + */ + guint8 general_inbld_flag; + + /** + * GstH265DecoderConfigRecord.general_level_idc: + * + * general level indication + */ + guint8 general_level_idc; + + /** + * GstH265DecoderConfigRecord.min_spatial_segmentation_idc: + * + * min spatial segmentation indication + */ + guint16 min_spatial_segmentation_idc; + + /** + * GstH265DecoderConfigRecord.parallelism_type: + * + * parallelism type + */ + guint8 parallelism_type; + + /** + * GstH265DecoderConfigRecord.chroma_format_idc: + * + * chroma format indication + */ + guint8 chroma_format_idc; + + /** + * GstH265DecoderConfigRecord.bit_depth_luma_minus8: + * + * bit depth luma minus 8 + */ + guint8 bit_depth_luma_minus8; + + /** + * GstH265DecoderConfigRecord.bit_depth_chroma_minus8: + * + * bit depth chroma minus 8 + */ + guint8 bit_depth_chroma_minus8; + + /** + * GstH265DecoderConfigRecord.avg_frame_rate: + * + * average frame rate in units of frames per 256 seconds, + * or 0 when unspecified + */ + guint16 avg_frame_rate; + + /** + * GstH265DecoderConfigRecord.constant_frame_rate: + * + * constant frame rate. + * 1: stream to which this configuration record applies is constante frame rate + * 2: representation of each temporal layer in the stream is constant frame rate + * 0: unspecified + */ + guint8 constant_frame_rate; + + /** + * GstH265DecoderConfigRecord.num_temporal_layers: + * + * 0: unknown whether the stream is temporally scalable + * otherwise: the number of temporal layers + */ + guint8 num_temporal_layers; + + /** + * GstH265DecoderConfigRecord.temporal_id_nested: + * + * 1: all SPSs that activated when the stream to this configuration record + * applies is decoded have sps_temporal_id_nesting_flags equal to 1 + * and temporal sub-layer up-switching to any higher temporal layer can + * be performed at any semple + * 0: unknown + */ + guint8 temporal_id_nested; + + /** + * GstH265DecoderConfigRecord.length_size_minus_one: + * + * indicates the length in bytes of nal unit length field. + * This value shall be one of 0, 1, or 3 corresponding to a length + * encoded with 1, 2, or 4 bytes, respectively + */ + guint8 length_size_minus_one; + + /** + * GstH265DecoderConfigRecord.nalu_array: + * + * Array of #GstH265DecoderConfigRecordNalUnitArray + */ + GArray *nalu_array; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstH265Parser: + * + * H265 NAL Parser (opaque structure). + */ +struct _GstH265Parser +{ + /*< private >*/ + GstH265VPS vps[GST_H265_MAX_VPS_COUNT]; + GstH265SPS sps[GST_H265_MAX_SPS_COUNT]; + GstH265PPS pps[GST_H265_MAX_PPS_COUNT]; + GstH265VPS *last_vps; + GstH265SPS *last_sps; + GstH265PPS *last_pps; +}; + +GST_CODEC_PARSERS_API +GstH265Parser * gst_h265_parser_new (void); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_identify_nalu (GstH265Parser * parser, + const guint8 * data, + guint offset, + gsize size, + GstH265NalUnit * nalu); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_identify_nalu_unchecked (GstH265Parser * parser, + const guint8 * data, + guint offset, + gsize size, + GstH265NalUnit * nalu); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_identify_nalu_hevc (GstH265Parser * parser, + const guint8 * data, + guint offset, + gsize size, + guint8 nal_length_size, + GstH265NalUnit * nalu); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_identify_and_split_nalu_hevc (GstH265Parser * parser, + const guint8 * data, + guint offset, + gsize size, + guint8 nal_length_size, + GArray * nalus, + gsize * consumed); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_nal (GstH265Parser * parser, + GstH265NalUnit * nalu); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_slice_hdr (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265SliceHdr * slice); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_slice_hdr_ext (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265SliceHdr * slice, + GstH265SPSEXT * sps_ext); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_vps (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265VPS * vps); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_sps (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265SPS * sps, + gboolean parse_vui_params); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_sps_ext (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265SPS * sps, + GstH265SPSEXT * sps_ext, + gboolean parse_vui_params); +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_pps (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265PPS * pps); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_sei (GstH265Parser * parser, + GstH265NalUnit * nalu, + GArray **messages); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_update_vps (GstH265Parser * parser, + GstH265VPS * vps); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_update_sps (GstH265Parser * parser, + GstH265SPS * sps); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_update_pps (GstH265Parser * parser, + GstH265PPS * pps); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_link_slice_hdr (GstH265Parser * parser, + GstH265SliceHdr * slice); + +GST_CODEC_PARSERS_API +void gst_h265_parser_free (GstH265Parser * parser); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parse_vps (GstH265NalUnit * nalu, + GstH265VPS * vps); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parse_sps (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265SPS * sps, + gboolean parse_vui_params); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parse_sps_ext (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265SPS * sps, + GstH265SPSEXT * sps_ext, + gboolean parse_vui_params); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parse_pps (GstH265Parser * parser, + GstH265NalUnit * nalu, + GstH265PPS * pps); + +GST_CODEC_PARSERS_API +gboolean gst_h265_slice_hdr_copy (GstH265SliceHdr * dst_slice, + const GstH265SliceHdr * src_slice); + +GST_CODEC_PARSERS_API +void gst_h265_slice_hdr_free (GstH265SliceHdr * slice_hdr); + +GST_CODEC_PARSERS_API +gboolean gst_h265_sei_copy (GstH265SEIMessage * dest_sei, + const GstH265SEIMessage * src_sei); + +GST_CODEC_PARSERS_API +void gst_h265_sei_free (GstH265SEIMessage * sei); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_4x4_get_zigzag_from_raster (guint8 out_quant[16], + const guint8 quant[16]); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_4x4_get_raster_from_zigzag (guint8 out_quant[16], + const guint8 quant[16]); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_8x8_get_zigzag_from_raster (guint8 out_quant[64], + const guint8 quant[64]); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_8x8_get_raster_from_zigzag (guint8 out_quant[64], + const guint8 quant[64]); + +#define gst_h265_quant_matrix_16x16_get_zigzag_from_raster \ + gst_h265_quant_matrix_8x8_get_zigzag_from_raster +#define gst_h265_quant_matrix_16x16_get_raster_from_zigzag \ + gst_h265_quant_matrix_8x8_get_raster_from_zigzag +#define gst_h265_quant_matrix_32x32_get_zigzag_from_raster \ + gst_h265_quant_matrix_8x8_get_zigzag_from_raster +#define gst_h265_quant_matrix_32x32_get_raster_from_zigzag \ + gst_h265_quant_matrix_8x8_get_raster_from_zigzag + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_4x4_get_uprightdiagonal_from_raster (guint8 out_quant[16], + const guint8 quant[16]); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_4x4_get_raster_from_uprightdiagonal (guint8 out_quant[16], + const guint8 quant[16]); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_8x8_get_uprightdiagonal_from_raster (guint8 out_quant[64], + const guint8 quant[64]); + +GST_CODEC_PARSERS_API +void gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal (guint8 out_quant[64], + const guint8 quant[64]); + +#define gst_h265_quant_matrix_16x16_get_uprightdiagonal_from_raster \ + gst_h265_quant_matrix_8x8_get_uprightdiagonal_from_raster +#define gst_h265_quant_matrix_16x16_get_raster_from_uprightdiagonal\ + gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal +#define gst_h265_quant_matrix_32x32_get_uprightdiagonal_from_raster \ + gst_h265_quant_matrix_8x8_get_uprightdiagonal_from_raster +#define gst_h265_quant_matrix_32x32_get_raster_from_uprightdiagonal\ + gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal + +GST_CODEC_PARSERS_API +GstH265Profile gst_h265_profile_tier_level_get_profile (const GstH265ProfileTierLevel * ptl); + +GST_CODEC_PARSERS_API +const gchar * gst_h265_profile_to_string (GstH265Profile profile); + +GST_CODEC_PARSERS_API +GstH265Profile gst_h265_profile_from_string (const gchar * string); + +GST_CODEC_PARSERS_API +const gchar * gst_h265_slice_type_to_string (GstH265SliceType slice_type); + +GST_CODEC_PARSERS_API +GstMemory * gst_h265_create_sei_memory (guint8 layer_id, + guint8 temporal_id_plus1, + guint8 start_code_prefix_length, + GArray * messages); + +GST_CODEC_PARSERS_API +GstMemory * gst_h265_create_sei_memory_hevc (guint8 layer_id, + guint8 temporal_id_plus1, + guint8 nal_length_size, + GArray * messages); + +GST_CODEC_PARSERS_API +GstBuffer * gst_h265_parser_insert_sei (GstH265Parser * parser, + GstBuffer * au, + GstMemory * sei); + +GST_CODEC_PARSERS_API +GstBuffer * gst_h265_parser_insert_sei_hevc (GstH265Parser * parser, + guint8 nal_length_size, + GstBuffer * au, + GstMemory * sei); + +GST_CODEC_PARSERS_API +GstH265Profile gst_h265_get_profile_from_sps (GstH265SPS * sps); + +GST_CODEC_PARSERS_API +void gst_h265_decoder_config_record_free (GstH265DecoderConfigRecord * config); + +GST_CODEC_PARSERS_API +GstH265ParserResult gst_h265_parser_parse_decoder_config_record (GstH265Parser * parser, + const guint8 * data, + gsize size, + GstH265DecoderConfigRecord ** config); + +G_END_DECLS +#endif diff --git a/src/h265_parser/nalutils.c b/src/h265_parser/nalutils.c new file mode 100644 index 0000000..014e820 --- /dev/null +++ b/src/h265_parser/nalutils.c @@ -0,0 +1,545 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * Some bits C-c,C-v'ed and s/4/3 from h264parse and videoparsers/h264parse.c: + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Nokia Corporation + * + * (C) 2005 Michal Benes + * (C) 2008 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * Common code for NAL parsing from h264 and h265 parsers. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "nalutils.h" + +/****** Nal parser ******/ + +void +nal_reader_init (NalReader * nr, const guint8 * data, guint size) +{ + nr->data = data; + nr->size = size; + nr->n_epb = 0; + + nr->byte = 0; + nr->bits_in_cache = 0; + /* fill with something other than 0 to detect emulation prevention bytes */ + nr->first_byte = 0xff; + nr->epb_cache = 0xff; + nr->cache = 0xff; +} + +gboolean +nal_reader_read (NalReader * nr, guint nbits) +{ + if (G_UNLIKELY (nr->byte * 8 + (nbits - nr->bits_in_cache) > nr->size * 8)) { + GST_DEBUG ("Can not read %u bits, bits in cache %u, Byte * 8 %u, size in " + "bits %u", nbits, nr->bits_in_cache, nr->byte * 8, nr->size * 8); + return FALSE; + } + + while (nr->bits_in_cache < nbits) { + guint8 byte; + + next_byte: + if (G_UNLIKELY (nr->byte >= nr->size)) + return FALSE; + + byte = nr->data[nr->byte++]; + nr->epb_cache = (nr->epb_cache << 8) | byte; + + /* check if the byte is a emulation_prevention_three_byte */ + if ((nr->epb_cache & 0xffffff) == 0x3) { + nr->n_epb++; + goto next_byte; + } + nr->cache = (nr->cache << 8) | nr->first_byte; + nr->first_byte = byte; + nr->bits_in_cache += 8; + } + + return TRUE; +} + +/* Skips the specified amount of bits. This is only suitable to a + cacheable number of bits */ +gboolean +nal_reader_skip (NalReader * nr, guint nbits) +{ + g_assert (nbits <= 8 * sizeof (nr->cache)); + + if (G_UNLIKELY (!nal_reader_read (nr, nbits))) + return FALSE; + + nr->bits_in_cache -= nbits; + + return TRUE; +} + +/* Generic version to skip any number of bits */ +gboolean +nal_reader_skip_long (NalReader * nr, guint nbits) +{ + /* Leave out enough bits in the cache once we are finished */ + const guint skip_size = 4 * sizeof (nr->cache); + guint remaining = nbits; + + nbits %= skip_size; + while (remaining > 0) { + if (!nal_reader_skip (nr, nbits)) + return FALSE; + remaining -= nbits; + nbits = skip_size; + } + return TRUE; +} + +guint +nal_reader_get_pos (const NalReader * nr) +{ + return nr->byte * 8 - nr->bits_in_cache; +} + +guint +nal_reader_get_remaining (const NalReader * nr) +{ + return (nr->size - nr->byte) * 8 + nr->bits_in_cache; +} + +guint +nal_reader_get_epb_count (const NalReader * nr) +{ + return nr->n_epb; +} + +#define NAL_READER_READ_BITS(bits) \ +gboolean \ +nal_reader_get_bits_uint##bits (NalReader *nr, guint##bits *val, guint nbits) \ +{ \ + guint shift; \ + \ + if (!nal_reader_read (nr, nbits)) \ + return FALSE; \ + \ + /* bring the required bits down and truncate */ \ + shift = nr->bits_in_cache - nbits; \ + *val = nr->first_byte >> shift; \ + \ + *val |= nr->cache << (8 - shift); \ + /* mask out required bits */ \ + if (nbits < bits) \ + *val &= ((guint##bits)1 << nbits) - 1; \ + \ + nr->bits_in_cache = shift; \ + \ + return TRUE; \ +} \ + +NAL_READER_READ_BITS (8); +NAL_READER_READ_BITS (16); +NAL_READER_READ_BITS (32); + +#define NAL_READER_PEEK_BITS(bits) \ +gboolean \ +nal_reader_peek_bits_uint##bits (const NalReader *nr, guint##bits *val, guint nbits) \ +{ \ + NalReader tmp; \ + \ + tmp = *nr; \ + return nal_reader_get_bits_uint##bits (&tmp, val, nbits); \ +} + +NAL_READER_PEEK_BITS (8); + +gboolean +nal_reader_get_ue (NalReader * nr, guint32 * val) +{ + guint i = 0; + guint8 bit; + guint32 value; + + if (G_UNLIKELY (!nal_reader_get_bits_uint8 (nr, &bit, 1))) + return FALSE; + + while (bit == 0) { + i++; + if (G_UNLIKELY (!nal_reader_get_bits_uint8 (nr, &bit, 1))) + return FALSE; + } + + if (G_UNLIKELY (i > 31)) + return FALSE; + + if (G_UNLIKELY (!nal_reader_get_bits_uint32 (nr, &value, i))) + return FALSE; + + *val = (1 << i) - 1 + value; + + return TRUE; +} + +gboolean +nal_reader_get_se (NalReader * nr, gint32 * val) +{ + guint32 value; + + if (G_UNLIKELY (!nal_reader_get_ue (nr, &value))) + return FALSE; + + if (value % 2) + *val = (value / 2) + 1; + else + *val = -(value / 2); + + return TRUE; +} + +gboolean +nal_reader_is_byte_aligned (NalReader * nr) +{ + if (nr->bits_in_cache != 0) + return FALSE; + return TRUE; +} + +gboolean +nal_reader_has_more_data (NalReader * nr) +{ + NalReader nr_tmp; + guint remaining, nbits; + guint8 rbsp_stop_one_bit, zero_bits; + + remaining = nal_reader_get_remaining (nr); + if (remaining == 0) + return FALSE; + + nr_tmp = *nr; + nr = &nr_tmp; + + /* The spec defines that more_rbsp_data() searches for the last bit + equal to 1, and that it is the rbsp_stop_one_bit. Subsequent bits + until byte boundary is reached shall be zero. + + This means that more_rbsp_data() is FALSE if the next bit is 1 + and the remaining bits until byte boundary are zero. One way to + be sure that this bit was the very last one, is that every other + bit after we reached byte boundary are also set to zero. + Otherwise, if the next bit is 0 or if there are non-zero bits + afterwards, then then we have more_rbsp_data() */ + if (!nal_reader_get_bits_uint8 (nr, &rbsp_stop_one_bit, 1)) + return FALSE; + if (!rbsp_stop_one_bit) + return TRUE; + + nbits = --remaining % 8; + while (remaining > 0) { + if (!nal_reader_get_bits_uint8 (nr, &zero_bits, nbits)) + return FALSE; + if (zero_bits != 0) + return TRUE; + remaining -= nbits; + nbits = 8; + } + return FALSE; +} + +/*********** end of nal parser ***************/ + +gint +scan_for_start_codes (const guint8 * data, guint size) +{ + GstByteReader br; + gst_byte_reader_init (&br, data, size); + + /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */ + return gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100, + 0, size); +} + +void +nal_writer_init (NalWriter * nw, guint nal_prefix_size, gboolean packetized) +{ + g_return_if_fail (nw != NULL); + g_return_if_fail ((packetized && nal_prefix_size > 1 && nal_prefix_size < 5) + || (!packetized && (nal_prefix_size == 3 || nal_prefix_size == 4))); + + gst_bit_writer_init (&nw->bw); + nw->nal_prefix_size = nal_prefix_size; + nw->packetized = packetized; +} + +void +nal_writer_reset (NalWriter * nw) +{ + g_return_if_fail (nw != NULL); + + gst_bit_writer_reset (&nw->bw); + memset (nw, 0, sizeof (NalWriter)); +} + +gboolean +nal_writer_do_rbsp_trailing_bits (NalWriter * nw) +{ + g_return_val_if_fail (nw != NULL, FALSE); + + if (!gst_bit_writer_put_bits_uint8 (&nw->bw, 1, 1)) { + GST_WARNING ("Cannot put trailing bits"); + return FALSE; + } + + if (!gst_bit_writer_align_bytes (&nw->bw, 0)) { + GST_WARNING ("Cannot put align bits"); + return FALSE; + } + + return TRUE; +} + +static gpointer +nal_writer_create_nal_data (NalWriter * nw, guint32 * ret_size) +{ + GstBitWriter bw; + gint i; + guint8 *src, *dst; + gsize size; + gpointer data; + + /* scan to put emulation_prevention_three_byte */ + size = GST_BIT_WRITER_BIT_SIZE (&nw->bw) >> 3; + src = GST_BIT_WRITER_DATA (&nw->bw); + + gst_bit_writer_init_with_size (&bw, size + nw->nal_prefix_size, FALSE); + for (i = 0; i < nw->nal_prefix_size - 1; i++) + gst_bit_writer_put_bits_uint8 (&bw, 0, 8); + gst_bit_writer_put_bits_uint8 (&bw, 1, 8); + + for (i = 0; i < size; i++) { + guint pos = (GST_BIT_WRITER_BIT_SIZE (&bw) >> 3); + dst = GST_BIT_WRITER_DATA (&bw); + if (pos >= nw->nal_prefix_size + 2 && + dst[pos - 2] == 0 && dst[pos - 1] == 0 && src[i] <= 0x3) { + gst_bit_writer_put_bits_uint8 (&bw, 0x3, 8); + } + + gst_bit_writer_put_bits_uint8 (&bw, src[i], 8); + } + + *ret_size = bw.bit_size >> 3; + data = gst_bit_writer_reset_and_get_data (&bw); + + if (nw->packetized) { + size = *ret_size - nw->nal_prefix_size; + + switch (nw->nal_prefix_size) { + case 1: + GST_WRITE_UINT8 (data, size); + break; + case 2: + GST_WRITE_UINT16_BE (data, size); + break; + case 3: + GST_WRITE_UINT24_BE (data, size); + break; + case 4: + GST_WRITE_UINT32_BE (data, size); + break; + default: + g_assert_not_reached (); + break; + } + } + + return data; +} + +GstMemory * +nal_writer_reset_and_get_memory (NalWriter * nw) +{ + guint32 size = 0; + GstMemory *ret = NULL; + gpointer data; + + g_return_val_if_fail (nw != NULL, NULL); + + if ((GST_BIT_WRITER_BIT_SIZE (&nw->bw) >> 3) == 0) { + GST_WARNING ("No written byte"); + goto done; + } + + if ((GST_BIT_WRITER_BIT_SIZE (&nw->bw) & 0x7) != 0) { + GST_WARNING ("Written stream is not byte aligned"); + if (!nal_writer_do_rbsp_trailing_bits (nw)) + goto done; + } + + data = nal_writer_create_nal_data (nw, &size); + if (!data) { + GST_WARNING ("Failed to create nal data"); + goto done; + } + + ret = gst_memory_new_wrapped (0, data, size, 0, size, data, g_free); + +done: + gst_bit_writer_reset (&nw->bw); + + return ret; +} + +guint8 * +nal_writer_reset_and_get_data (NalWriter * nw, guint32 * ret_size) +{ + guint32 size = 0; + guint8 *data = NULL; + + g_return_val_if_fail (nw != NULL, NULL); + g_return_val_if_fail (ret_size != NULL, NULL); + + *ret_size = 0; + + if ((GST_BIT_WRITER_BIT_SIZE (&nw->bw) >> 3) == 0) { + GST_WARNING ("No written byte"); + goto done; + } + + if ((GST_BIT_WRITER_BIT_SIZE (&nw->bw) & 0x7) != 0) { + GST_WARNING ("Written stream is not byte aligned"); + if (!nal_writer_do_rbsp_trailing_bits (nw)) + goto done; + } + + data = nal_writer_create_nal_data (nw, &size); + if (!data) { + GST_WARNING ("Failed to create nal data"); + goto done; + } + + *ret_size = size; + +done: + gst_bit_writer_reset (&nw->bw); + + return data; +} + +gboolean +nal_writer_put_bits_uint8 (NalWriter * nw, guint8 value, guint nbits) +{ + g_return_val_if_fail (nw != NULL, FALSE); + + if (!gst_bit_writer_put_bits_uint8 (&nw->bw, value, nbits)) + return FALSE; + + return TRUE; +} + +gboolean +nal_writer_put_bits_uint16 (NalWriter * nw, guint16 value, guint nbits) +{ + g_return_val_if_fail (nw != NULL, FALSE); + + if (!gst_bit_writer_put_bits_uint16 (&nw->bw, value, nbits)) + return FALSE; + + return TRUE; +} + +gboolean +nal_writer_put_bits_uint32 (NalWriter * nw, guint32 value, guint nbits) +{ + g_return_val_if_fail (nw != NULL, FALSE); + + if (!gst_bit_writer_put_bits_uint32 (&nw->bw, value, nbits)) + return FALSE; + + return TRUE; +} + +gboolean +nal_writer_put_bytes (NalWriter * nw, const guint8 * data, guint nbytes) +{ + g_return_val_if_fail (nw != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (nbytes != 0, FALSE); + + if (!gst_bit_writer_put_bytes (&nw->bw, data, nbytes)) + return FALSE; + + return TRUE; +} + +gboolean +nal_writer_put_ue (NalWriter * nw, guint32 value) +{ + guint leading_zeros; + guint rest; + + g_return_val_if_fail (nw != NULL, FALSE); + + count_exp_golomb_bits (value, &leading_zeros, &rest); + + /* write leading zeros */ + if (leading_zeros) { + if (!nal_writer_put_bits_uint32 (nw, 0, leading_zeros)) + return FALSE; + } + + /* write the rest */ + if (!nal_writer_put_bits_uint32 (nw, value + 1, rest)) + return FALSE; + + return TRUE; +} + +gboolean +count_exp_golomb_bits (guint32 value, guint * leading_zeros, guint * rest) +{ + guint32 x; + guint count = 0; + + /* https://en.wikipedia.org/wiki/Exponential-Golomb_coding */ + /* count bits of value + 1 */ + x = value + 1; + while (x) { + count++; + x >>= 1; + } + + if (leading_zeros) { + if (count > 1) + *leading_zeros = count - 1; + else + *leading_zeros = 0; + } + + if (rest) { + *rest = count; + } + + return TRUE; +} diff --git a/src/h265_parser/nalutils.h b/src/h265_parser/nalutils.h new file mode 100644 index 0000000..9c169cb --- /dev/null +++ b/src/h265_parser/nalutils.h @@ -0,0 +1,269 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * Some bits C-c,C-v'ed and s/4/3 from h264parse and videoparsers/h264parse.c: + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Nokia Corporation + * + * (C) 2005 Michal Benes + * (C) 2008 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * Common code for NAL parsing from h264 and h265 parsers. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +typedef struct +{ + const guint8 *data; + guint size; + + guint n_epb; /* Number of emulation prevention bytes */ + guint byte; /* Byte position */ + guint bits_in_cache; /* bitpos in the cache of next bit */ + guint8 first_byte; + guint32 epb_cache; /* cache 3 bytes to check emulation prevention bytes */ + guint64 cache; /* cached bytes */ +} NalReader; + +typedef struct +{ + GstBitWriter bw; + + guint nal_prefix_size; + gboolean packetized; +} NalWriter; + +G_GNUC_INTERNAL +void nal_reader_init (NalReader * nr, const guint8 * data, guint size); + +G_GNUC_INTERNAL +gboolean nal_reader_read (NalReader * nr, guint nbits); + +G_GNUC_INTERNAL +gboolean nal_reader_skip (NalReader * nr, guint nbits); + +G_GNUC_INTERNAL +gboolean nal_reader_skip_long (NalReader * nr, guint nbits); + +G_GNUC_INTERNAL +guint nal_reader_get_pos (const NalReader * nr); + +G_GNUC_INTERNAL +guint nal_reader_get_remaining (const NalReader * nr); + +G_GNUC_INTERNAL +guint nal_reader_get_epb_count (const NalReader * nr); + +G_GNUC_INTERNAL +gboolean nal_reader_is_byte_aligned (NalReader * nr); + +G_GNUC_INTERNAL +gboolean nal_reader_has_more_data (NalReader * nr); + +#define NAL_READER_READ_BITS_H(bits) \ +G_GNUC_INTERNAL \ +gboolean nal_reader_get_bits_uint##bits (NalReader *nr, guint##bits *val, guint nbits) + +NAL_READER_READ_BITS_H (8); +NAL_READER_READ_BITS_H (16); +NAL_READER_READ_BITS_H (32); + +#define NAL_READER_PEEK_BITS_H(bits) \ +G_GNUC_INTERNAL \ +gboolean nal_reader_peek_bits_uint##bits (const NalReader *nr, guint##bits *val, guint nbits) + +NAL_READER_PEEK_BITS_H (8); + +G_GNUC_INTERNAL +gboolean nal_reader_get_ue (NalReader * nr, guint32 * val); + +G_GNUC_INTERNAL +gboolean nal_reader_get_se (NalReader * nr, gint32 * val); + +#define CHECK_ALLOWED_MAX_WITH_DEBUG(dbg, val, max) { \ + if (val > max) { \ + GST_WARNING ("value for '" dbg "' greater than max. value: %d, max %d", \ + val, max); \ + goto error; \ + } \ +} +#define CHECK_ALLOWED_MAX(val, max) \ + CHECK_ALLOWED_MAX_WITH_DEBUG (G_STRINGIFY (val), val, max) + +#define CHECK_ALLOWED_WITH_DEBUG(dbg, val, min, max) { \ + if (val < min || val > max) { \ + GST_WARNING ("value for '" dbg "' not in allowed range. value: %d, range %d-%d", \ + val, min, max); \ + goto error; \ + } \ +} +#define CHECK_ALLOWED(val, min, max) \ + CHECK_ALLOWED_WITH_DEBUG (G_STRINGIFY (val), val, min, max) + +#define READ_UINT8(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint8 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint8 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT16(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint16 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint16 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT32(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint32 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint32 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT64(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint64 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint32 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UE(nr, val) { \ + if (!nal_reader_get_ue (nr, &val)) { \ + GST_WARNING ("failed to read UE for '" G_STRINGIFY (val) "'"); \ + goto error; \ + } \ +} + +#define READ_UE_ALLOWED(nr, val, min, max) { \ + guint32 tmp; \ + READ_UE (nr, tmp); \ + CHECK_ALLOWED_WITH_DEBUG (G_STRINGIFY (val), tmp, min, max); \ + val = tmp; \ +} + +#define READ_UE_MAX(nr, val, max) { \ + guint32 tmp; \ + READ_UE (nr, tmp); \ + CHECK_ALLOWED_MAX_WITH_DEBUG (G_STRINGIFY (val), tmp, max); \ + val = tmp; \ +} + +#define READ_SE(nr, val) { \ + if (!nal_reader_get_se (nr, &val)) { \ + GST_WARNING ("failed to read SE for '" G_STRINGIFY (val) "'"); \ + goto error; \ + } \ +} + +#define READ_SE_ALLOWED(nr, val, min, max) { \ + gint32 tmp; \ + READ_SE (nr, tmp); \ + CHECK_ALLOWED_WITH_DEBUG (G_STRINGIFY (val), tmp, min, max); \ + val = tmp; \ +} + +G_GNUC_INTERNAL +gint scan_for_start_codes (const guint8 * data, guint size); + +G_GNUC_INTERNAL +void nal_writer_init (NalWriter * nw, guint nal_prefix_size, gboolean packetized); + +G_GNUC_INTERNAL +void nal_writer_reset (NalWriter * nw); + +G_GNUC_INTERNAL +gboolean nal_writer_do_rbsp_trailing_bits (NalWriter * nw); + +G_GNUC_INTERNAL +GstMemory * nal_writer_reset_and_get_memory (NalWriter * nw); + +G_GNUC_INTERNAL +guint8 * nal_writer_reset_and_get_data (NalWriter * nw, guint32 * ret_size); + +G_GNUC_INTERNAL +gboolean nal_writer_put_bits_uint8 (NalWriter * nw, guint8 value, guint nbits); + +G_GNUC_INTERNAL +gboolean nal_writer_put_bits_uint16 (NalWriter * nw, guint16 value, guint nbits); + +G_GNUC_INTERNAL +gboolean nal_writer_put_bits_uint32 (NalWriter * nw, guint32 value, guint nbits); + +G_GNUC_INTERNAL +gboolean nal_writer_put_bytes (NalWriter * nw, const guint8 * data, guint nbytes); + +G_GNUC_INTERNAL +gboolean nal_writer_put_ue (NalWriter * nw, guint32 value); + +G_GNUC_INTERNAL +gboolean count_exp_golomb_bits (guint32 value, guint * leading_zeros, guint * rest); + +#define WRITE_UINT8(nw, val, nbits) { \ + if (!nal_writer_put_bits_uint8 (nw, val, nbits)) { \ + GST_WARNING ("failed to write uint8 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define WRITE_UINT16(nw, val, nbits) { \ + if (!nal_writer_put_bits_uint16 (nw, val, nbits)) { \ + GST_WARNING ("failed to write uint16 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define WRITE_UINT32(nw, val, nbits) { \ + if (!nal_writer_put_bits_uint32 (nw, val, nbits)) { \ + GST_WARNING ("failed to write uint32 for '" G_STRINGIFY (val) "', nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define WRITE_BYTES(nw, data, nbytes) { \ + if (!nal_writer_put_bytes (nw, data, nbytes)) { \ + GST_WARNING ("failed to write bytes for '" G_STRINGIFY (val) "', nbits: %d", nbytes); \ + goto error; \ + } \ +} + +#define WRITE_UE(nw, val) { \ + if (!nal_writer_put_ue (nw, val)) { \ + GST_WARNING ("failed to write ue for '" G_STRINGIFY (val) "'"); \ + goto error; \ + } \ +} + +static inline guint32 div_ceil (guint32 a, guint32 b) +{ + /* http://blog.pkh.me/p/36-figuring-out-round%2C-floor-and-ceil-with-integer-division.html */ + g_assert (b > 0); + return a / b + (a % b > 0); +}