2014-10-16 08:34:01 +00:00
/* GStreamer
* Copyright ( C ) < 2006 > Wim Taymans < wim . taymans @ gmail . com >
* Copyright ( C ) < 2014 > Jurgen Slowack < jurgenslowack @ gmail . com >
*
* 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
# include <stdio.h>
# include <string.h>
# include <gst/base/gstbitreader.h>
# include <gst/rtp/gstrtpbuffer.h>
2015-08-14 10:49:51 +00:00
# include <gst/video/video.h>
2014-10-16 08:34:01 +00:00
# include "gstrtph265depay.h"
GST_DEBUG_CATEGORY_STATIC ( rtph265depay_debug ) ;
# define GST_CAT_DEFAULT (rtph265depay_debug)
/* This is what we'll default to when downstream hasn't
* expressed a restriction or preference via caps */
# define DEFAULT_BYTE_STREAM TRUE
# define DEFAULT_ACCESS_UNIT FALSE
/* 3 zero bytes syncword */
static const guint8 sync_bytes [ ] = { 0 , 0 , 0 , 1 } ;
static GstStaticPadTemplate gst_rtp_h265_depay_src_template =
GST_STATIC_PAD_TEMPLATE ( " src " ,
GST_PAD_SRC ,
GST_PAD_ALWAYS ,
GST_STATIC_CAPS (
/* FIXME - hvc1 and hev1 formats are not supported yet */
/*"video/x-h265, "
" stream-format = (string) hvc1, alignment = (string) au; "
" video/x-h265, "
" stream-format = (string) hev1, alignment = (string) au; " */
" video/x-h265, "
" stream-format = (string) byte-stream, alignment = (string) { nal, au } " )
) ;
static GstStaticPadTemplate gst_rtp_h265_depay_sink_template =
GST_STATIC_PAD_TEMPLATE ( " sink " ,
GST_PAD_SINK ,
GST_PAD_ALWAYS ,
GST_STATIC_CAPS ( " application/x-rtp, "
" media = (string) \" video \" , "
" clock-rate = (int) 90000, " " encoding-name = (string) \" H265 \" " )
/** optional parameters **/
/* "profile-space = (int) [ 0, 3 ], " */
/* "profile-id = (int) [ 0, 31 ], " */
/* "tier-flag = (int) [ 0, 1 ], " */
/* "level-id = (int) [ 0, 255 ], " */
/* "interop-constraints = (string) ANY, " */
/* "profile-compatibility-indicator = (string) ANY, " */
/* "sprop-sub-layer-id = (int) [ 0, 6 ], " */
/* "recv-sub-layer-id = (int) [ 0, 6 ], " */
/* "max-recv-level-id = (int) [ 0, 255 ], " */
/* "tx-mode = (string) {MST , SST}, " */
/* "sprop-vps = (string) ANY, " */
/* "sprop-sps = (string) ANY, " */
/* "sprop-pps = (string) ANY, " */
/* "sprop-sei = (string) ANY, " */
/* "max-lsr = (int) ANY, " */ /* MUST be in the range of MaxLumaSR to 16 * MaxLumaSR, inclusive */
/* "max-lps = (int) ANY, " */ /* MUST be in the range of MaxLumaPS to 16 * MaxLumaPS, inclusive */
/* "max-cpb = (int) ANY, " */ /* MUST be in the range of MaxCPB to 16 * MaxCPB, inclusive */
/* "max-dpb = (int) [1, 16], " */
/* "max-br = (int) ANY, " */ /* MUST be in the range of MaxBR to 16 * MaxBR, inclusive, for the highest level */
/* "max-tr = (int) ANY, " */ /* MUST be in the range of MaxTileRows to 16 * MaxTileRows, inclusive, for the highest level */
/* "max-tc = (int) ANY, " */ /* MUST be in the range of MaxTileCols to 16 * MaxTileCols, inclusive, for the highest level */
/* "max-fps = (int) ANY, " */
/* "sprop-max-don-diff = (int) [0, 32767], " */
/* "sprop-depack-buf-nalus = (int) [0, 32767], " */
/* "sprop-depack-buf-nalus = (int) [0, 4294967295], " */
/* "depack-buf-cap = (int) [1, 4294967295], " */
/* "sprop-segmentation-id = (int) [0, 3], " */
/* "sprop-spatial-segmentation-idc = (string) ANY, " */
/* "dec-parallel-cap = (string) ANY, " */
) ;
# define gst_rtp_h265_depay_parent_class parent_class
G_DEFINE_TYPE ( GstRtpH265Depay , gst_rtp_h265_depay ,
GST_TYPE_RTP_BASE_DEPAYLOAD ) ;
static void gst_rtp_h265_depay_finalize ( GObject * object ) ;
static GstStateChangeReturn gst_rtp_h265_depay_change_state ( GstElement *
element , GstStateChange transition ) ;
static GstBuffer * gst_rtp_h265_depay_process ( GstRTPBaseDepayload * depayload ,
2015-08-12 14:49:50 +00:00
GstRTPBuffer * rtp ) ;
2014-10-16 08:34:01 +00:00
static gboolean gst_rtp_h265_depay_setcaps ( GstRTPBaseDepayload * filter ,
GstCaps * caps ) ;
static gboolean gst_rtp_h265_depay_handle_event ( GstRTPBaseDepayload * depay ,
GstEvent * event ) ;
static void
gst_rtp_h265_depay_class_init ( GstRtpH265DepayClass * klass )
{
GObjectClass * gobject_class ;
GstElementClass * gstelement_class ;
GstRTPBaseDepayloadClass * gstrtpbasedepayload_class ;
gobject_class = ( GObjectClass * ) klass ;
gstelement_class = ( GstElementClass * ) klass ;
gstrtpbasedepayload_class = ( GstRTPBaseDepayloadClass * ) klass ;
gobject_class - > finalize = gst_rtp_h265_depay_finalize ;
gst_element_class_add_pad_template ( gstelement_class ,
gst_static_pad_template_get ( & gst_rtp_h265_depay_src_template ) ) ;
gst_element_class_add_pad_template ( gstelement_class ,
gst_static_pad_template_get ( & gst_rtp_h265_depay_sink_template ) ) ;
gst_element_class_set_static_metadata ( gstelement_class ,
" RTP H265 depayloader " , " Codec/Depayloader/Network/RTP " ,
" Extracts H265 video from RTP packets (draft-ietf-payload-rtp-h265-03.txt) " ,
" Jurgen Slowack <jurgenslowack@gmail.com> " ) ;
gstelement_class - > change_state = gst_rtp_h265_depay_change_state ;
2015-08-12 14:49:50 +00:00
gstrtpbasedepayload_class - > process_rtp_packet = gst_rtp_h265_depay_process ;
2014-10-16 08:34:01 +00:00
gstrtpbasedepayload_class - > set_caps = gst_rtp_h265_depay_setcaps ;
gstrtpbasedepayload_class - > handle_event = gst_rtp_h265_depay_handle_event ;
}
static void
gst_rtp_h265_depay_init ( GstRtpH265Depay * rtph265depay )
{
rtph265depay - > adapter = gst_adapter_new ( ) ;
rtph265depay - > picture_adapter = gst_adapter_new ( ) ;
rtph265depay - > byte_stream = DEFAULT_BYTE_STREAM ;
rtph265depay - > stream_format = ( gchar * ) g_malloc ( 10 ) ;
rtph265depay - > merge = DEFAULT_ACCESS_UNIT ;
rtph265depay - > vps = g_ptr_array_new_with_free_func (
( GDestroyNotify ) gst_buffer_unref ) ;
rtph265depay - > sps = g_ptr_array_new_with_free_func (
( GDestroyNotify ) gst_buffer_unref ) ;
rtph265depay - > pps = g_ptr_array_new_with_free_func (
( GDestroyNotify ) gst_buffer_unref ) ;
}
static void
gst_rtp_h265_depay_reset ( GstRtpH265Depay * rtph265depay )
{
gst_adapter_clear ( rtph265depay - > adapter ) ;
rtph265depay - > wait_start = TRUE ;
gst_adapter_clear ( rtph265depay - > picture_adapter ) ;
rtph265depay - > picture_start = FALSE ;
rtph265depay - > last_keyframe = FALSE ;
rtph265depay - > last_ts = 0 ;
rtph265depay - > current_fu_type = 0 ;
rtph265depay - > new_codec_data = FALSE ;
g_ptr_array_set_size ( rtph265depay - > vps , 0 ) ;
g_ptr_array_set_size ( rtph265depay - > sps , 0 ) ;
g_ptr_array_set_size ( rtph265depay - > pps , 0 ) ;
}
static void
gst_rtp_h265_depay_finalize ( GObject * object )
{
GstRtpH265Depay * rtph265depay ;
rtph265depay = GST_RTP_H265_DEPAY ( object ) ;
if ( rtph265depay - > codec_data )
gst_buffer_unref ( rtph265depay - > codec_data ) ;
g_free ( rtph265depay - > stream_format ) ;
g_object_unref ( rtph265depay - > adapter ) ;
g_object_unref ( rtph265depay - > picture_adapter ) ;
g_ptr_array_free ( rtph265depay - > vps , TRUE ) ;
g_ptr_array_free ( rtph265depay - > sps , TRUE ) ;
g_ptr_array_free ( rtph265depay - > pps , TRUE ) ;
G_OBJECT_CLASS ( parent_class ) - > finalize ( object ) ;
}
static void
gst_rtp_h265_depay_negotiate ( GstRtpH265Depay * rtph265depay )
{
GstCaps * caps ;
gint byte_stream = - 1 ;
gint merge = - 1 ;
caps =
gst_pad_get_allowed_caps ( GST_RTP_BASE_DEPAYLOAD_SRCPAD ( rtph265depay ) ) ;
GST_DEBUG_OBJECT ( rtph265depay , " allowed caps: % " GST_PTR_FORMAT , caps ) ;
if ( caps ) {
if ( gst_caps_get_size ( caps ) > 0 ) {
GstStructure * s = gst_caps_get_structure ( caps , 0 ) ;
const gchar * str = NULL ;
if ( ( str = gst_structure_get_string ( s , " stream-format " ) ) ) {
strcpy ( rtph265depay - > stream_format , str ) ;
if ( strcmp ( str , " hev1 " ) = = 0 ) {
byte_stream = FALSE ;
} else if ( strcmp ( str , " hvc1 " ) = = 0 ) {
byte_stream = FALSE ;
} else if ( strcmp ( str , " byte-stream " ) = = 0 ) {
byte_stream = TRUE ;
} else {
GST_DEBUG_OBJECT ( rtph265depay , " unknown stream-format: %s " , str ) ;
}
}
if ( ( str = gst_structure_get_string ( s , " alignment " ) ) ) {
if ( strcmp ( str , " au " ) = = 0 ) {
merge = TRUE ;
} else if ( strcmp ( str , " nal " ) = = 0 ) {
merge = FALSE ;
} else {
GST_DEBUG_OBJECT ( rtph265depay , " unknown alignment: %s " , str ) ;
}
}
}
gst_caps_unref ( caps ) ;
}
if ( byte_stream ! = - 1 ) {
GST_DEBUG_OBJECT ( rtph265depay , " downstream requires byte-stream %d " ,
byte_stream ) ;
rtph265depay - > byte_stream = byte_stream ;
} else {
GST_DEBUG_OBJECT ( rtph265depay , " defaulting to byte-stream %d " ,
DEFAULT_BYTE_STREAM ) ;
strcpy ( rtph265depay - > stream_format , " byte-stream " ) ;
rtph265depay - > byte_stream = DEFAULT_BYTE_STREAM ;
}
if ( merge ! = - 1 ) {
GST_DEBUG_OBJECT ( rtph265depay , " downstream requires merge %d " , merge ) ;
rtph265depay - > merge = merge ;
} else {
GST_DEBUG_OBJECT ( rtph265depay , " defaulting to merge %d " ,
DEFAULT_ACCESS_UNIT ) ;
rtph265depay - > merge = DEFAULT_ACCESS_UNIT ;
}
}
/* Stolen from bad/gst/mpegtsdemux/payloader_parsers.c */
/* variable length Exp-Golomb parsing according to H.265 spec section 9.2*/
static gboolean
read_golomb ( GstBitReader * br , guint32 * value )
{
guint8 b , leading_zeros = - 1 ;
* value = 1 ;
for ( b = 0 ; ! b ; leading_zeros + + ) {
if ( ! gst_bit_reader_get_bits_uint8 ( br , & b , 1 ) )
return FALSE ;
* value * = 2 ;
}
* value = ( * value > > 1 ) - 1 ;
if ( leading_zeros > 0 ) {
guint32 tmp = 0 ;
if ( ! gst_bit_reader_get_bits_uint32 ( br , & tmp , leading_zeros ) )
return FALSE ;
* value + = tmp ;
}
return TRUE ;
}
static gboolean
parse_sps ( GstMapInfo * map , guint32 * sps_id )
{ /* To parse seq_parameter_set_id */
GstBitReader br = GST_BIT_READER_INIT ( map - > data + 15 ,
map - > size - 15 ) ;
if ( map - > size < 16 )
return FALSE ;
if ( ! read_golomb ( & br , sps_id ) )
return FALSE ;
return TRUE ;
}
static gboolean
parse_pps ( GstMapInfo * map , guint32 * sps_id , guint32 * pps_id )
{ /* To parse picture_parameter_set_id */
GstBitReader br = GST_BIT_READER_INIT ( map - > data + 2 ,
map - > size - 2 ) ;
if ( map - > size < 3 )
return FALSE ;
if ( ! read_golomb ( & br , pps_id ) )
return FALSE ;
if ( ! read_golomb ( & br , sps_id ) )
return FALSE ;
return TRUE ;
}
static gboolean
gst_rtp_h265_set_src_caps ( GstRtpH265Depay * rtph265depay )
{
gboolean res ;
GstCaps * srccaps ;
if ( ! rtph265depay - > byte_stream & &
( ! rtph265depay - > new_codec_data | |
rtph265depay - > vps - > len = = 0 | | rtph265depay - > sps - > len = = 0
| | rtph265depay - > pps - > len = = 0 ) )
return TRUE ;
srccaps = gst_caps_new_simple ( " video/x-h265 " ,
" stream-format " , G_TYPE_STRING ,
rtph265depay - > stream_format ,
" alignment " , G_TYPE_STRING , rtph265depay - > merge ? " au " : " nal " , NULL ) ;
if ( ! rtph265depay - > byte_stream ) {
GstBuffer * codec_data ;
gint i = 0 ;
gint len ;
guint num_vps = rtph265depay - > vps - > len ;
guint num_sps = rtph265depay - > sps - > len ;
guint num_pps = rtph265depay - > pps - > len ;
GstMapInfo map , nalmap ;
guint8 * data ;
gint nl ;
guint8 num_arrays = 0 ;
guint new_size ;
GstBitReader br ;
guint32 tmp ;
guint8 tmp8 = 0 ;
guint32 max_sub_layers_minus1 , temporal_id_nesting_flag , chroma_format_idc ,
bit_depth_luma_minus8 , bit_depth_chroma_minus8 ,
min_spatial_segmentation_idc ;
/* Fixme: Current implementation is not embedding SEI in codec_data */
if ( num_sps = = 0 )
return FALSE ;
/* start with 23 bytes header */
len = 23 ;
num_arrays = ( num_vps > 0 ) + ( num_sps > 0 ) + ( num_pps > 0 ) ;
len + = num_arrays ;
/* add size of vps, sps & pps */
for ( i = 0 ; i < num_vps ; i + + )
len + = 2 + gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > vps , i ) ) ;
for ( i = 0 ; i < num_sps ; i + + )
len + = 2 + gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > sps , i ) ) ;
for ( i = 0 ; i < num_pps ; i + + )
len + = 2 + gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > pps , i ) ) ;
GST_DEBUG_OBJECT ( rtph265depay ,
" constructing codec_data: num_vps =%d num_sps=%d, num_pps=%d " , num_vps ,
num_sps , num_pps ) ;
codec_data = gst_buffer_new_and_alloc ( len ) ;
g_debug ( " alloc_len: %u " , len ) ;
gst_buffer_map ( codec_data , & map , GST_MAP_READWRITE ) ;
data = map . data ;
memset ( data , 0 , map . size ) ;
/* Parsing sps to get the info required further on */
gst_buffer_map ( g_ptr_array_index ( rtph265depay - > sps , 0 ) , & nalmap ,
GST_MAP_READ ) ;
max_sub_layers_minus1 = ( ( nalmap . data [ 2 ] ) > > 1 ) & 0x07 ;
temporal_id_nesting_flag = nalmap . data [ 2 ] & 0x01 ;
gst_bit_reader_init ( & br , nalmap . data + 15 , nalmap . size - 15 ) ;
read_golomb ( & br , & tmp ) ; /* sps_seq_parameter_set_id */
read_golomb ( & br , & chroma_format_idc ) ; /* chroma_format_idc */
if ( chroma_format_idc = = 3 )
gst_bit_reader_get_bits_uint8 ( & br , & tmp8 , 1 ) ; /* separate_colour_plane_flag */
read_golomb ( & br , & tmp ) ; /* pic_width_in_luma_samples */
read_golomb ( & br , & tmp ) ; /* pic_height_in_luma_samples */
gst_bit_reader_get_bits_uint8 ( & br , & tmp8 , 1 ) ; /* conformance_window_flag */
if ( tmp8 ) {
read_golomb ( & br , & tmp ) ; /* conf_win_left_offset */
read_golomb ( & br , & tmp ) ; /* conf_win_right_offset */
read_golomb ( & br , & tmp ) ; /* conf_win_top_offset */
read_golomb ( & br , & tmp ) ; /* conf_win_bottom_offset */
}
read_golomb ( & br , & bit_depth_luma_minus8 ) ; /* bit_depth_luma_minus8 */
read_golomb ( & br , & bit_depth_chroma_minus8 ) ; /* bit_depth_chroma_minus8 */
GST_DEBUG_OBJECT ( rtph265depay ,
" Ignoring min_spatial_segmentation for now (assuming zero) " ) ;
min_spatial_segmentation_idc = 0 ; /* NOTE - we ignore this for now, but in a perfect world, we should continue parsing to obtain the real value */
nl = nalmap . size ;
gst_buffer_unmap ( g_ptr_array_index ( rtph265depay - > sps , 0 ) , & nalmap ) ;
/* HEVCDecoderConfigurationVersion = 1 */
data [ 0 ] = 1 ;
/* Copy from profile_tier_level (Rec. ITU-T H.265 (04/2013) section 7.3.3
*
* profile_space | tier_flat | profile_idc |
* profile_compatibility_flags | constraint_indicator_flags |
* level_idc | progressive_source_flag | interlaced_source_flag
* non_packed_constraint_flag | frame_only_constraint_flag
* reserved_zero_44bits | level_idc */
gst_buffer_map ( g_ptr_array_index ( rtph265depay - > sps , 0 ) , & nalmap ,
GST_MAP_READ ) ;
for ( i = 0 ; i < 12 ; i + + )
data [ i + 1 ] = nalmap . data [ i ] ;
gst_buffer_unmap ( g_ptr_array_index ( rtph265depay - > sps , 0 ) , & nalmap ) ;
/* min_spatial_segmentation_idc */
GST_WRITE_UINT16_BE ( data + 13 , min_spatial_segmentation_idc ) ;
data [ 13 ] | = 0xf0 ;
data [ 15 ] = 0xfc ; /* keeping parrallelismType as zero (unknown) */
data [ 16 ] = 0xfc | chroma_format_idc ;
data [ 17 ] = 0xf8 | bit_depth_luma_minus8 ;
data [ 18 ] = 0xf8 | bit_depth_chroma_minus8 ;
data [ 19 ] = 0x00 ; /* keep avgFrameRate as unspecified */
data [ 20 ] = 0x00 ; /* keep avgFrameRate as unspecified */
/* constFrameRate(2 bits): 0, stream may or may not be of constant framerate
* numTemporalLayers ( 3 bits ) : number of temporal layers , value from SPS
* TemporalIdNested ( 1 bit ) : sps_temporal_id_nesting_flag from SPS
* lengthSizeMinusOne ( 2 bits ) : plus 1 indicates the length of the NALUnitLength */
data [ 21 ] =
0x00 | ( ( max_sub_layers_minus1 +
1 ) < < 3 ) | ( temporal_id_nesting_flag < < 2 ) | ( nl - 1 ) ;
GST_WRITE_UINT8 ( data + 22 , num_arrays ) ; /* numOfArrays */
data + = 23 ;
/* copy all VPS */
if ( num_vps > 0 ) {
/* array_completeness | reserved_zero bit | nal_unit_type */
data [ 0 ] = 0x00 | 0x20 ;
data + + ;
GST_WRITE_UINT16_BE ( data , num_vps ) ;
data + = 2 ;
for ( i = 0 ; i < num_vps ; i + + ) {
gsize nal_size =
gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > vps , i ) ) ;
GST_WRITE_UINT16_BE ( data , nal_size ) ;
gst_buffer_extract ( g_ptr_array_index ( rtph265depay - > vps , i ) , 0 ,
data + 2 , nal_size ) ;
data + = 2 + nal_size ;
GST_DEBUG_OBJECT ( rtph265depay , " Copied VPS %d of length %u " , i ,
( guint ) nal_size ) ;
}
}
/* copy all SPS */
if ( num_sps > 0 ) {
/* array_completeness | reserved_zero bit | nal_unit_type */
data [ 0 ] = 0x00 | 0x21 ;
data + + ;
GST_WRITE_UINT16_BE ( data , num_sps ) ;
data + = 2 ;
for ( i = 0 ; i < num_sps ; i + + ) {
gsize nal_size =
gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > sps , i ) ) ;
GST_WRITE_UINT16_BE ( data , nal_size ) ;
gst_buffer_extract ( g_ptr_array_index ( rtph265depay - > sps , i ) , 0 ,
data + 2 , nal_size ) ;
data + = 2 + nal_size ;
GST_DEBUG_OBJECT ( rtph265depay , " Copied SPS %d of length %u " , i ,
( guint ) nal_size ) ;
}
}
/* copy all PPS */
if ( num_pps > 0 ) {
/* array_completeness | reserved_zero bit | nal_unit_type */
data [ 0 ] = 0x00 | 0x22 ;
data + + ;
GST_WRITE_UINT16_BE ( data , num_pps ) ;
data + = 2 ;
for ( i = 0 ; i < num_pps ; i + + ) {
gsize nal_size =
gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > pps , i ) ) ;
GST_WRITE_UINT16_BE ( data , nal_size ) ;
gst_buffer_extract ( g_ptr_array_index ( rtph265depay - > pps , i ) , 0 ,
data + 2 , nal_size ) ;
data + = 2 + nal_size ;
GST_DEBUG_OBJECT ( rtph265depay , " Copied PPS %d of length %u " , i ,
( guint ) nal_size ) ;
}
}
new_size = data - map . data ;
gst_buffer_unmap ( codec_data , & map ) ;
gst_buffer_set_size ( codec_data , new_size ) ;
gst_caps_set_simple ( srccaps ,
" codec_data " , GST_TYPE_BUFFER , codec_data , NULL ) ;
gst_buffer_unref ( codec_data ) ;
}
2015-08-12 16:22:42 +00:00
if ( gst_pad_has_current_caps ( GST_RTP_BASE_DEPAYLOAD_SRCPAD ( rtph265depay ) ) ) {
GstCaps * old_caps =
gst_pad_get_current_caps ( GST_RTP_BASE_DEPAYLOAD_SRCPAD ( rtph265depay ) ) ;
/* Only update the caps if they are not equal. For
* AVC we don ' t update caps if only the codec_data
* changes . This is the same behaviour as in h264parse
* and gstrtph264depay
*/
if ( rtph265depay - > byte_stream ) {
if ( ! gst_caps_is_equal ( srccaps , old_caps ) )
res =
gst_pad_set_caps ( GST_RTP_BASE_DEPAYLOAD_SRCPAD ( rtph265depay ) ,
srccaps ) ;
else
res = TRUE ;
} else {
GstCaps * tmp_caps = gst_caps_copy ( srccaps ) ;
GstStructure * old_s , * tmp_s ;
old_s = gst_caps_get_structure ( old_caps , 0 ) ;
tmp_s = gst_caps_get_structure ( tmp_caps , 0 ) ;
if ( gst_structure_has_field ( old_s , " codec_data " ) )
gst_structure_set_value ( tmp_s , " codec_data " ,
gst_structure_get_value ( old_s , " codec_data " ) ) ;
if ( ! gst_caps_is_equal ( old_caps , tmp_caps ) )
res =
gst_pad_set_caps ( GST_RTP_BASE_DEPAYLOAD_SRCPAD ( rtph265depay ) ,
srccaps ) ;
else
res = TRUE ;
gst_caps_unref ( tmp_caps ) ;
}
} else {
res =
gst_pad_set_caps ( GST_RTP_BASE_DEPAYLOAD_SRCPAD ( rtph265depay ) ,
srccaps ) ;
}
2014-10-16 08:34:01 +00:00
gst_caps_unref ( srccaps ) ;
2015-08-12 15:11:00 +00:00
/* Insert SPS and PPS into the stream on next opportunity */
2015-08-12 16:54:52 +00:00
if ( rtph265depay - > sps - > len > 0 | | rtph265depay - > pps - > len > 0 ) {
2015-08-12 15:11:00 +00:00
gint i ;
GstBuffer * codec_data ;
GstMapInfo map ;
guint8 * data ;
guint len = 0 ;
for ( i = 0 ; i < rtph265depay - > sps - > len ; i + + ) {
len + = 4 + gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > sps , i ) ) ;
}
for ( i = 0 ; i < rtph265depay - > pps - > len ; i + + ) {
len + = 4 + gst_buffer_get_size ( g_ptr_array_index ( rtph265depay - > pps , i ) ) ;
}
codec_data = gst_buffer_new_and_alloc ( len ) ;
gst_buffer_map ( codec_data , & map , GST_MAP_WRITE ) ;
data = map . data ;
for ( i = 0 ; i < rtph265depay - > sps - > len ; i + + ) {
GstBuffer * sps_buf = g_ptr_array_index ( rtph265depay - > sps , i ) ;
guint sps_size = gst_buffer_get_size ( sps_buf ) ;
if ( rtph265depay - > byte_stream )
memcpy ( data , sync_bytes , sizeof ( sync_bytes ) ) ;
else
GST_WRITE_UINT32_BE ( data , sps_size ) ;
gst_buffer_extract ( sps_buf , 0 , data + 4 , - 1 ) ;
data + = 4 + sps_size ;
}
for ( i = 0 ; i < rtph265depay - > pps - > len ; i + + ) {
GstBuffer * pps_buf = g_ptr_array_index ( rtph265depay - > pps , i ) ;
guint pps_size = gst_buffer_get_size ( pps_buf ) ;
if ( rtph265depay - > byte_stream )
memcpy ( data , sync_bytes , sizeof ( sync_bytes ) ) ;
else
GST_WRITE_UINT32_BE ( data , pps_size ) ;
gst_buffer_extract ( pps_buf , 0 , data + 4 , - 1 ) ;
data + = 4 + pps_size ;
}
gst_buffer_unmap ( codec_data , & map ) ;
if ( rtph265depay - > codec_data )
gst_buffer_unref ( rtph265depay - > codec_data ) ;
rtph265depay - > codec_data = codec_data ;
}
2014-10-16 08:34:01 +00:00
if ( res )
rtph265depay - > new_codec_data = FALSE ;
return res ;
}
gboolean
gst_rtp_h265_add_vps_sps_pps ( GstElement * rtph265 , GPtrArray * vps_array ,
GPtrArray * sps_array , GPtrArray * pps_array , GstBuffer * nal )
{
GstMapInfo map ;
guchar type ;
guint i ;
gst_buffer_map ( nal , & map , GST_MAP_READ ) ;
type = ( map . data [ 0 ] > > 1 ) & 0x3f ;
if ( type = = GST_H265_VPS_NUT ) {
guint32 vps_id = ( map . data [ 2 ] > > 4 ) & 0x0f ;
for ( i = 0 ; i < vps_array - > len ; i + + ) {
GstBuffer * vps = g_ptr_array_index ( vps_array , i ) ;
GstMapInfo vpsmap ;
guint32 tmp_vps_id ;
gst_buffer_map ( vps , & vpsmap , GST_MAP_READ ) ;
tmp_vps_id = ( vpsmap . data [ 2 ] > > 4 ) & 0x0f ;
if ( vps_id = = tmp_vps_id ) {
if ( map . size = = vpsmap . size & &
memcmp ( map . data , vpsmap . data , vpsmap . size ) = = 0 ) {
GST_LOG_OBJECT ( rtph265 , " Unchanged VPS %u, not updating " , vps_id ) ;
gst_buffer_unmap ( vps , & vpsmap ) ;
goto drop ;
} else {
gst_buffer_unmap ( vps , & vpsmap ) ;
g_ptr_array_remove_index_fast ( vps_array , i ) ;
g_ptr_array_add ( vps_array , nal ) ;
GST_LOG_OBJECT ( rtph265 , " Modified VPS %u, replacing " , vps_id ) ;
goto done ;
}
}
gst_buffer_unmap ( vps , & vpsmap ) ;
}
GST_LOG_OBJECT ( rtph265 , " Adding new VPS %u " , vps_id ) ;
g_ptr_array_add ( vps_array , nal ) ;
} else if ( type = = GST_H265_SPS_NUT ) {
guint32 sps_id ;
if ( ! parse_sps ( & map , & sps_id ) ) {
GST_WARNING_OBJECT ( rtph265 , " Invalid SPS, "
" can't parse seq_parameter_set_id " ) ;
goto drop ;
}
for ( i = 0 ; i < sps_array - > len ; i + + ) {
GstBuffer * sps = g_ptr_array_index ( sps_array , i ) ;
GstMapInfo spsmap ;
guint32 tmp_sps_id ;
gst_buffer_map ( sps , & spsmap , GST_MAP_READ ) ;
parse_sps ( & spsmap , & tmp_sps_id ) ;
if ( sps_id = = tmp_sps_id ) {
if ( map . size = = spsmap . size & &
memcmp ( map . data , spsmap . data , spsmap . size ) = = 0 ) {
GST_LOG_OBJECT ( rtph265 , " Unchanged SPS %u, not updating " , sps_id ) ;
gst_buffer_unmap ( sps , & spsmap ) ;
goto drop ;
} else {
gst_buffer_unmap ( sps , & spsmap ) ;
g_ptr_array_remove_index_fast ( sps_array , i ) ;
g_ptr_array_add ( sps_array , nal ) ;
GST_LOG_OBJECT ( rtph265 , " Modified SPS %u, replacing " , sps_id ) ;
goto done ;
}
}
gst_buffer_unmap ( sps , & spsmap ) ;
}
GST_LOG_OBJECT ( rtph265 , " Adding new SPS %u " , sps_id ) ;
g_ptr_array_add ( sps_array , nal ) ;
} else if ( type = = GST_H265_PPS_NUT ) {
guint32 sps_id ;
guint32 pps_id ;
if ( ! parse_pps ( & map , & sps_id , & pps_id ) ) {
GST_WARNING_OBJECT ( rtph265 , " Invalid PPS, "
" can't parse seq_parameter_set_id or pic_parameter_set_id " ) ;
goto drop ;
}
for ( i = 0 ; i < pps_array - > len ; i + + ) {
GstBuffer * pps = g_ptr_array_index ( pps_array , i ) ;
GstMapInfo ppsmap ;
guint32 tmp_sps_id ;
guint32 tmp_pps_id ;
gst_buffer_map ( pps , & ppsmap , GST_MAP_READ ) ;
parse_pps ( & ppsmap , & tmp_sps_id , & tmp_pps_id ) ;
2015-08-12 15:43:48 +00:00
if ( pps_id = = tmp_pps_id ) {
2014-10-16 08:34:01 +00:00
if ( map . size = = ppsmap . size & &
memcmp ( map . data , ppsmap . data , ppsmap . size ) = = 0 ) {
GST_LOG_OBJECT ( rtph265 , " Unchanged PPS %u:%u, not updating " , sps_id ,
pps_id ) ;
gst_buffer_unmap ( pps , & ppsmap ) ;
goto drop ;
} else {
gst_buffer_unmap ( pps , & ppsmap ) ;
g_ptr_array_remove_index_fast ( pps_array , i ) ;
g_ptr_array_add ( pps_array , nal ) ;
GST_LOG_OBJECT ( rtph265 , " Modified PPS %u:%u, replacing " ,
sps_id , pps_id ) ;
goto done ;
}
}
gst_buffer_unmap ( pps , & ppsmap ) ;
}
GST_LOG_OBJECT ( rtph265 , " Adding new PPS %u:%i " , sps_id , pps_id ) ;
g_ptr_array_add ( pps_array , nal ) ;
} else {
goto drop ;
}
done :
gst_buffer_unmap ( nal , & map ) ;
return TRUE ;
drop :
gst_buffer_unmap ( nal , & map ) ;
gst_buffer_unref ( nal ) ;
return FALSE ;
}
static void
gst_rtp_h265_depay_add_vps_sps_pps ( GstRtpH265Depay * rtph265depay ,
GstBuffer * nal )
{
if ( gst_rtp_h265_add_vps_sps_pps ( GST_ELEMENT ( rtph265depay ) ,
rtph265depay - > vps , rtph265depay - > sps , rtph265depay - > pps , nal ) )
rtph265depay - > new_codec_data = TRUE ;
}
static gboolean
gst_rtp_h265_depay_setcaps ( GstRTPBaseDepayload * depayload , GstCaps * caps )
{
gint clock_rate ;
GstStructure * structure = gst_caps_get_structure ( caps , 0 ) ;
GstRtpH265Depay * rtph265depay ;
const gchar * ps ;
GstBuffer * codec_data ;
GstMapInfo map ;
guint8 * ptr ;
rtph265depay = GST_RTP_H265_DEPAY ( depayload ) ;
if ( ! gst_structure_get_int ( structure , " clock-rate " , & clock_rate ) )
clock_rate = 90000 ;
depayload - > clock_rate = clock_rate ;
/* Base64 encoded, comma separated config NALs */
ps = gst_structure_get_string ( structure , " sprop-parameter-sets " ) ;
/* negotiate with downstream w.r.t. output format and alignment */
gst_rtp_h265_depay_negotiate ( rtph265depay ) ;
if ( rtph265depay - > byte_stream & & ps ! = NULL ) {
/* for bytestream we only need the parameter sets but we don't error out
* when they are not there , we assume they are in the stream . */
gchar * * params ;
guint len , total ;
gint i ;
params = g_strsplit ( ps , " , " , 0 ) ;
/* count total number of bytes in base64. Also include the sync bytes in
* front of the params . */
len = 0 ;
for ( i = 0 ; params [ i ] ; i + + ) {
len + = strlen ( params [ i ] ) ;
len + = sizeof ( sync_bytes ) ;
}
/* we seriously overshoot the length, but it's fine. */
codec_data = gst_buffer_new_and_alloc ( len ) ;
gst_buffer_map ( codec_data , & map , GST_MAP_WRITE ) ;
ptr = map . data ;
total = 0 ;
for ( i = 0 ; params [ i ] ; i + + ) {
guint save = 0 ;
gint state = 0 ;
GST_DEBUG_OBJECT ( depayload , " decoding param %d (%s) " , i , params [ i ] ) ;
memcpy ( ptr , sync_bytes , sizeof ( sync_bytes ) ) ;
ptr + = sizeof ( sync_bytes ) ;
len =
g_base64_decode_step ( params [ i ] , strlen ( params [ i ] ) , ptr , & state ,
& save ) ;
GST_DEBUG_OBJECT ( depayload , " decoded %d bytes " , len ) ;
total + = len + sizeof ( sync_bytes ) ;
ptr + = len ;
}
gst_buffer_unmap ( codec_data , & map ) ;
gst_buffer_resize ( codec_data , 0 , total ) ;
g_strfreev ( params ) ;
/* keep the codec_data, we need to send it as the first buffer. We cannot
* push it in the adapter because the adapter might be flushed on discont .
*/
if ( rtph265depay - > codec_data )
gst_buffer_unref ( rtph265depay - > codec_data ) ;
rtph265depay - > codec_data = codec_data ;
} else if ( ! rtph265depay - > byte_stream ) {
gchar * * params ;
gint i ;
if ( ps = = NULL )
goto incomplete_caps ;
params = g_strsplit ( ps , " , " , 0 ) ;
GST_DEBUG_OBJECT ( depayload , " we have %d params " , g_strv_length ( params ) ) ;
/* start with 23 bytes header */
for ( i = 0 ; params [ i ] ; i + + ) {
GstBuffer * nal ;
GstMapInfo nalmap ;
gsize nal_len ;
guint save = 0 ;
gint state = 0 ;
nal_len = strlen ( params [ i ] ) ;
nal = gst_buffer_new_and_alloc ( nal_len ) ;
gst_buffer_map ( nal , & nalmap , GST_MAP_READWRITE ) ;
nal_len =
g_base64_decode_step ( params [ i ] , nal_len , nalmap . data , & state , & save ) ;
GST_DEBUG_OBJECT ( depayload , " adding param %d as %s " , i ,
( ( ( nalmap . data [ 0 ] > > 1 ) & 0x3f ) = =
32 ) ? " VPS " : ( ( ( nalmap . data [ 0 ] > > 1 ) & 0x3f ) = =
33 ) ? " SPS " : " PPS " ) ;
gst_buffer_unmap ( nal , & nalmap ) ;
gst_buffer_set_size ( nal , nal_len ) ;
gst_rtp_h265_depay_add_vps_sps_pps ( rtph265depay , nal ) ;
}
g_strfreev ( params ) ;
if ( rtph265depay - > sps - > len = = 0 | | rtph265depay - > pps - > len = = 0 )
goto incomplete_caps ;
}
return gst_rtp_h265_set_src_caps ( rtph265depay ) ;
/* ERRORS */
incomplete_caps :
{
GST_DEBUG_OBJECT ( depayload , " we have incomplete caps, "
" doing setcaps later " ) ;
return TRUE ;
}
}
static GstBuffer *
gst_rtp_h265_complete_au ( GstRtpH265Depay * rtph265depay ,
GstClockTime * out_timestamp , gboolean * out_keyframe )
{
guint outsize ;
GstBuffer * outbuf ;
/* we had a picture in the adapter and we completed it */
GST_DEBUG_OBJECT ( rtph265depay , " taking completed AU " ) ;
outsize = gst_adapter_available ( rtph265depay - > picture_adapter ) ;
outbuf = gst_adapter_take_buffer ( rtph265depay - > picture_adapter , outsize ) ;
* out_timestamp = rtph265depay - > last_ts ;
* out_keyframe = rtph265depay - > last_keyframe ;
rtph265depay - > last_keyframe = FALSE ;
rtph265depay - > picture_start = FALSE ;
return outbuf ;
}
/* VPS/SPS/PPS/RADL/TSA/RASL/IDR/CRA is considered key, all others DELTA;
* so downstream waiting for keyframe can pick up at VPS / SPS / PPS / IDR */
# define NAL_TYPE_IS_PARAMETER_SET(nt) ( ((nt) == GST_H265_VPS_NUT)\
| | ( ( nt ) = = GST_H265_SPS_NUT ) \
| | ( ( nt ) = = GST_H265_PPS_NUT ) )
# define NAL_TYPE_IS_CODED_SLICE_SEGMENT(nt) ( ((nt) == GST_H265_NAL_SLICE_TRAIL_N)\
| | ( ( nt ) = = GST_H265_NAL_SLICE_TRAIL_R ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_TSA_N ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_TSA_R ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_STSA_N ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_STSA_R ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_RASL_N ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_RASL_R ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_BLA_W_LP ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_BLA_W_RADL ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_BLA_N_LP ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_IDR_W_RADL ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_IDR_N_LP ) \
| | ( ( nt ) = = GST_H265_NAL_SLICE_CRA_NUT ) )
# define NAL_TYPE_IS_KEY(nt) (NAL_TYPE_IS_PARAMETER_SET(nt) || NAL_TYPE_IS_CODED_SLICE_SEGMENT(nt))
2015-08-14 10:49:51 +00:00
static gboolean
foreach_metadata_copy ( GstBuffer * inbuf , GstMeta * * meta , gpointer user_data )
{
CopyMetaData * data = user_data ;
GstElement * element = data - > element ;
GstBuffer * outbuf = data - > outbuf ;
const GstMetaInfo * info = ( * meta ) - > info ;
const gchar * const * tags = gst_meta_api_type_get_tags ( info - > api ) ;
if ( ! tags | | ( g_strv_length ( ( gchar * * ) tags ) = = 1
& & gst_meta_api_type_has_tag ( info - > api ,
g_quark_from_string ( GST_META_TAG_VIDEO_STR ) ) ) ) {
GstMetaTransformCopy copy_data = { FALSE , 0 , - 1 } ;
GST_DEBUG_OBJECT ( element , " copy metadata %s " , g_type_name ( info - > api ) ) ;
/* simply copy then */
info - > transform_func ( outbuf , * meta , inbuf ,
_gst_meta_transform_copy , & copy_data ) ;
} else {
GST_DEBUG_OBJECT ( element , " not copying metadata %s " ,
g_type_name ( info - > api ) ) ;
}
return TRUE ;
}
/* TODO: Should probably make copy_tag an array at some point */
void
gst_rtp_copy_meta ( GstElement * element , GstBuffer * outbuf , GstBuffer * inbuf ,
GQuark copy_tag )
{
CopyMetaData data = { element , outbuf , copy_tag } ;
gst_buffer_foreach_meta ( inbuf , foreach_metadata_copy , & data ) ;
}
static gboolean
foreach_metadata_drop ( GstBuffer * inbuf , GstMeta * * meta , gpointer user_data )
{
GstRtpH265Depay * depay = user_data ;
const GstMetaInfo * info = ( * meta ) - > info ;
const gchar * const * tags = gst_meta_api_type_get_tags ( info - > api ) ;
if ( ! tags | | ( g_strv_length ( ( gchar * * ) tags ) = = 1
& & gst_meta_api_type_has_tag ( info - > api ,
g_quark_from_string ( GST_META_TAG_VIDEO_STR ) ) ) ) {
GST_DEBUG_OBJECT ( depay , " keeping metadata %s " , g_type_name ( info - > api ) ) ;
} else {
GST_DEBUG_OBJECT ( depay , " dropping metadata %s " , g_type_name ( info - > api ) ) ;
* meta = NULL ;
}
return TRUE ;
}
2014-10-16 08:34:01 +00:00
static GstBuffer *
gst_rtp_h265_depay_handle_nal ( GstRtpH265Depay * rtph265depay , GstBuffer * nal ,
GstClockTime in_timestamp , gboolean marker )
{
GstRTPBaseDepayload * depayload = GST_RTP_BASE_DEPAYLOAD ( rtph265depay ) ;
gint nal_type ;
GstMapInfo map ;
GstBuffer * outbuf = NULL ;
GstClockTime out_timestamp ;
gboolean keyframe , out_keyframe ;
gst_buffer_map ( nal , & map , GST_MAP_READ ) ;
if ( G_UNLIKELY ( map . size < 5 ) )
goto short_nal ;
nal_type = ( map . data [ 4 ] > > 1 ) & 0x3f ;
GST_DEBUG_OBJECT ( rtph265depay , " handle NAL type %d (RTP marker bit %d) " ,
nal_type , marker ) ;
keyframe = NAL_TYPE_IS_KEY ( nal_type ) ;
out_keyframe = keyframe ;
out_timestamp = in_timestamp ;
if ( ! rtph265depay - > byte_stream ) {
if ( NAL_TYPE_IS_PARAMETER_SET ( nal_type ) ) {
gst_rtp_h265_depay_add_vps_sps_pps ( rtph265depay ,
gst_buffer_copy_region ( nal , GST_BUFFER_COPY_ALL ,
4 , gst_buffer_get_size ( nal ) - 4 ) ) ;
gst_buffer_unmap ( nal , & map ) ;
gst_buffer_unref ( nal ) ;
return NULL ;
} else if ( rtph265depay - > sps - > len = = 0 | | rtph265depay - > pps - > len = = 0 ) {
/* Down push down any buffer in non-bytestream mode if the SPS/PPS haven't
* go through yet
*/
gst_pad_push_event ( GST_RTP_BASE_DEPAYLOAD_SINKPAD ( depayload ) ,
gst_event_new_custom ( GST_EVENT_CUSTOM_UPSTREAM ,
gst_structure_new ( " GstForceKeyUnit " ,
" all-headers " , G_TYPE_BOOLEAN , TRUE , NULL ) ) ) ;
gst_buffer_unmap ( nal , & map ) ;
gst_buffer_unref ( nal ) ;
return NULL ;
}
if ( rtph265depay - > new_codec_data & &
rtph265depay - > sps - > len > 0 & & rtph265depay - > pps - > len > 0 )
gst_rtp_h265_set_src_caps ( rtph265depay ) ;
}
if ( rtph265depay - > merge ) {
gboolean start = FALSE , complete = FALSE ;
/* marker bit isn't mandatory so in the following code we try to detect
* an AU boundary ( see H .265 spec section 7.4 .2 .4 .4 ) */
if ( ! marker ) {
if ( NAL_TYPE_IS_CODED_SLICE_SEGMENT ( nal_type ) ) {
/* A NAL unit (X) ends an access unit if the next-occurring VCL NAL unit (Y) has the high-order bit of the first byte after its NAL unit header equal to 1 */
start = TRUE ;
if ( ( ( map . data [ 6 ] > > 7 ) & 0x01 ) = = 1 ) {
complete = TRUE ;
}
complete = TRUE ;
} else if ( ( nal_type > = 32 & & nal_type < = 35 )
2015-01-08 15:27:44 +00:00
| | nal_type = = 39 | | ( nal_type > = 41 & & nal_type < = 44 )
| | ( nal_type > = 48 & & nal_type < = 55 ) ) {
2014-10-16 08:34:01 +00:00
/* VPS, SPS, PPS, SEI, ... terminate an access unit */
complete = TRUE ;
}
GST_DEBUG_OBJECT ( depayload , " start %d, complete %d " , start , complete ) ;
if ( complete & & rtph265depay - > picture_start )
outbuf = gst_rtp_h265_complete_au ( rtph265depay , & out_timestamp ,
& out_keyframe ) ;
}
/* add to adapter */
gst_buffer_unmap ( nal , & map ) ;
GST_DEBUG_OBJECT ( depayload , " adding NAL to picture adapter " ) ;
gst_adapter_push ( rtph265depay - > picture_adapter , nal ) ;
rtph265depay - > last_ts = in_timestamp ;
rtph265depay - > last_keyframe | = keyframe ;
rtph265depay - > picture_start | = start ;
if ( marker )
outbuf = gst_rtp_h265_complete_au ( rtph265depay , & out_timestamp ,
& out_keyframe ) ;
} else {
/* no merge, output is input nal */
GST_DEBUG_OBJECT ( depayload , " using NAL as output " ) ;
outbuf = nal ;
gst_buffer_unmap ( nal , & map ) ;
}
if ( outbuf ) {
/* prepend codec_data */
if ( rtph265depay - > codec_data ) {
GST_DEBUG_OBJECT ( depayload , " prepending codec_data " ) ;
2015-08-14 10:49:51 +00:00
gst_rtp_copy_meta ( GST_ELEMENT_CAST ( rtph265depay ) ,
rtph265depay - > codec_data , outbuf ,
g_quark_from_static_string ( GST_META_TAG_VIDEO_STR ) ) ;
2014-10-16 08:34:01 +00:00
outbuf = gst_buffer_append ( rtph265depay - > codec_data , outbuf ) ;
rtph265depay - > codec_data = NULL ;
out_keyframe = TRUE ;
}
outbuf = gst_buffer_make_writable ( outbuf ) ;
2015-08-14 10:49:51 +00:00
gst_buffer_foreach_meta ( outbuf , foreach_metadata_drop , depayload ) ;
2015-08-12 14:14:50 +00:00
GST_BUFFER_PTS ( outbuf ) = out_timestamp ;
2014-10-16 08:34:01 +00:00
if ( out_keyframe )
GST_BUFFER_FLAG_UNSET ( outbuf , GST_BUFFER_FLAG_DELTA_UNIT ) ;
else
GST_BUFFER_FLAG_SET ( outbuf , GST_BUFFER_FLAG_DELTA_UNIT ) ;
}
return outbuf ;
/* ERRORS */
short_nal :
{
GST_WARNING_OBJECT ( depayload , " dropping short NAL " ) ;
gst_buffer_unmap ( nal , & map ) ;
gst_buffer_unref ( nal ) ;
return NULL ;
}
}
static GstBuffer *
gst_rtp_h265_push_fragmentation_unit ( GstRtpH265Depay * rtph265depay ,
gboolean send )
{
guint outsize ;
GstMapInfo map ;
GstBuffer * outbuf ;
outsize = gst_adapter_available ( rtph265depay - > adapter ) ;
outbuf = gst_adapter_take_buffer ( rtph265depay - > adapter , outsize ) ;
gst_buffer_map ( outbuf , & map , GST_MAP_WRITE ) ;
GST_DEBUG_OBJECT ( rtph265depay , " output %d bytes " , outsize ) ;
if ( rtph265depay - > byte_stream ) {
memcpy ( map . data , sync_bytes , sizeof ( sync_bytes ) ) ;
} else {
goto not_implemented ;
}
gst_buffer_unmap ( outbuf , & map ) ;
rtph265depay - > current_fu_type = 0 ;
outbuf = gst_rtp_h265_depay_handle_nal ( rtph265depay , outbuf ,
rtph265depay - > fu_timestamp , rtph265depay - > fu_marker ) ;
if ( send & & outbuf ) {
gst_rtp_base_depayload_push ( GST_RTP_BASE_DEPAYLOAD ( rtph265depay ) , outbuf ) ;
outbuf = NULL ;
}
return outbuf ;
not_implemented :
{
GST_ERROR_OBJECT ( rtph265depay ,
( " Only bytestream format is currently supported. " ) ) ;
gst_buffer_unmap ( outbuf , & map ) ;
return NULL ;
}
}
static GstBuffer *
2015-08-12 14:49:50 +00:00
gst_rtp_h265_depay_process ( GstRTPBaseDepayload * depayload , GstRTPBuffer * rtp )
2014-10-16 08:34:01 +00:00
{
GstRtpH265Depay * rtph265depay ;
2015-08-14 10:49:51 +00:00
GstBuffer * buf ;
2014-10-16 08:34:01 +00:00
GstBuffer * outbuf = NULL ;
guint8 nal_unit_type ;
rtph265depay = GST_RTP_H265_DEPAY ( depayload ) ;
/* flush remaining data on discont */
2015-08-12 14:49:50 +00:00
if ( GST_BUFFER_IS_DISCONT ( rtp - > buffer ) ) {
2014-10-16 08:34:01 +00:00
gst_adapter_clear ( rtph265depay - > adapter ) ;
rtph265depay - > wait_start = TRUE ;
rtph265depay - > current_fu_type = 0 ;
}
{
gint payload_len ;
guint8 * payload ;
guint header_len ;
GstMapInfo map ;
guint outsize , nalu_size ;
GstClockTime timestamp ;
gboolean marker ;
guint8 nuh_layer_id , nuh_temporal_id_plus1 ;
guint8 S , E ;
guint16 nal_header ;
2015-03-06 14:54:41 +00:00
#if 0
gboolean donl_present = FALSE ;
# endif
2014-10-16 08:34:01 +00:00
2015-08-12 14:49:50 +00:00
timestamp = GST_BUFFER_PTS ( rtp - > buffer ) ;
2014-10-16 08:34:01 +00:00
2015-08-12 14:49:50 +00:00
payload_len = gst_rtp_buffer_get_payload_len ( rtp ) ;
payload = gst_rtp_buffer_get_payload ( rtp ) ;
2015-08-14 10:49:51 +00:00
buf = gst_rtp_buffer_get_payload_buffer ( rtp ) ;
2015-08-12 14:49:50 +00:00
marker = gst_rtp_buffer_get_marker ( rtp ) ;
2014-10-16 08:34:01 +00:00
GST_DEBUG_OBJECT ( rtph265depay , " receiving %d bytes " , payload_len ) ;
if ( payload_len = = 0 )
goto empty_packet ;
/* +---------------+---------------+
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
* + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
* | F | Type | LayerId | TID |
* + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - +
*
* F must be 0.
*
*/
nal_unit_type = ( payload [ 0 ] > > 1 ) & 0x3f ;
nuh_layer_id = ( ( payload [ 0 ] & 0x01 ) < < 5 ) | ( payload [ 1 ] > > 3 ) ; /* should be zero for now but this could change in future HEVC extensions */
nuh_temporal_id_plus1 = payload [ 1 ] & 0x03 ;
/* At least two byte header with type */
header_len = 2 ;
GST_DEBUG_OBJECT ( rtph265depay ,
" NAL header nal_unit_type %d, nuh_temporal_id_plus1 %d " , nal_unit_type ,
nuh_temporal_id_plus1 ) ;
GST_FIXME_OBJECT ( rtph265depay , " Assuming DONL field is not present " ) ;
/* FIXME - assuming DONL field is not present for now */
/*donl_present = (tx-mode == "MST") || (sprop-max-don-diff > 0); */
/* If FU unit was being processed, but the current nal is of a different
* type . Assume that the remote payloader is buggy ( didn ' t set the end bit
* when the FU ended ) and send out what we gathered thusfar */
if ( G_UNLIKELY ( rtph265depay - > current_fu_type ! = 0 & &
nal_unit_type ! = rtph265depay - > current_fu_type ) )
gst_rtp_h265_push_fragmentation_unit ( rtph265depay , TRUE ) ;
switch ( nal_unit_type ) {
case 48 :
{
GST_DEBUG_OBJECT ( rtph265depay , " Processing aggregation packet " ) ;
/* Aggregation packet (section 4.7) */
/* An example of an AP packet containing two aggregation units
without the DONL and DOND fields
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| RTP Header |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| PayloadHdr ( Type = 48 ) | NALU 1 Size |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| NALU 1 HDR | |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + NALU 1 Data |
| . . . |
| |
+ + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| . . . | NALU 2 Size | NALU 2 HDR |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| NALU 2 HDR | |
+ - + - + - + - + - + - + - + - + NALU 2 Data |
| . . . |
| + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| : . . . OPTIONAL RTP padding |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
*/
/* strip headers */
payload + = header_len ;
payload_len - = header_len ;
rtph265depay - > wait_start = FALSE ;
2015-03-06 14:54:41 +00:00
#if 0
2014-10-16 08:34:01 +00:00
if ( donl_present )
goto not_implemented_donl_present ;
2015-03-06 14:54:41 +00:00
# endif
2014-10-16 08:34:01 +00:00
while ( payload_len > 2 ) {
nalu_size = ( payload [ 0 ] < < 8 ) | payload [ 1 ] ;
/* dont include nalu_size */
if ( nalu_size > ( payload_len - 2 ) )
nalu_size = payload_len - 2 ;
outsize = nalu_size + sizeof ( sync_bytes ) ;
outbuf = gst_buffer_new_and_alloc ( outsize ) ;
gst_buffer_map ( outbuf , & map , GST_MAP_WRITE ) ;
if ( rtph265depay - > byte_stream ) {
memcpy ( map . data , sync_bytes , sizeof ( sync_bytes ) ) ;
} else {
goto not_implemented ;
}
/* strip NALU size */
payload + = 2 ;
payload_len - = 2 ;
memcpy ( map . data + sizeof ( sync_bytes ) , payload , nalu_size ) ;
gst_buffer_unmap ( outbuf , & map ) ;
2015-08-14 10:49:51 +00:00
gst_rtp_copy_meta ( GST_ELEMENT_CAST ( rtph265depay ) , outbuf , buf ,
g_quark_from_static_string ( GST_META_TAG_VIDEO_STR ) ) ;
2014-10-16 08:34:01 +00:00
gst_adapter_push ( rtph265depay - > adapter , outbuf ) ;
payload + = nalu_size ;
payload_len - = nalu_size ;
}
outsize = gst_adapter_available ( rtph265depay - > adapter ) ;
2015-08-12 13:59:53 +00:00
if ( outsize > 0 ) {
outbuf = gst_adapter_take_buffer ( rtph265depay - > adapter , outsize ) ;
outbuf =
gst_rtp_h265_depay_handle_nal ( rtph265depay , outbuf , timestamp ,
marker ) ;
}
2014-10-16 08:34:01 +00:00
break ;
}
case 49 :
{
GST_DEBUG_OBJECT ( rtph265depay , " Processing Fragmentation Unit " ) ;
/* Fragmentation units (FUs) Section 4.8 */
/* The structure of a Fragmentation Unit (FU)
*
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| PayloadHdr ( Type = 49 ) | FU header | DONL ( cond ) |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - |
| DONL ( cond ) | |
| - + - + - + - + - + - + - + - + |
| FU payload |
| |
| + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| : . . . OPTIONAL RTP padding |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
*
*
*/
/* strip headers */
payload + = header_len ;
payload_len - = header_len ;
/* processing FU header */
S = ( payload [ 0 ] & 0x80 ) = = 0x80 ;
E = ( payload [ 0 ] & 0x40 ) = = 0x40 ;
GST_DEBUG_OBJECT ( rtph265depay ,
" FU header with S %d, E %d, nal_unit_type %d " , S , E ,
payload [ 0 ] & 0x3f ) ;
if ( rtph265depay - > wait_start & & ! S )
goto waiting_start ;
2015-03-06 14:54:41 +00:00
#if 0
2014-10-16 08:34:01 +00:00
if ( donl_present )
goto not_implemented_donl_present ;
2015-03-06 14:54:41 +00:00
# endif
2014-10-16 08:34:01 +00:00
if ( S ) {
GST_DEBUG_OBJECT ( rtph265depay , " Start of Fragmentation Unit " ) ;
/* If a new FU unit started, while still processing an older one.
* Assume that the remote payloader is buggy ( doesn ' t set the end
* bit ) and send out what we ' ve gathered thusfar */
if ( G_UNLIKELY ( rtph265depay - > current_fu_type ! = 0 ) )
gst_rtp_h265_push_fragmentation_unit ( rtph265depay , TRUE ) ;
rtph265depay - > current_fu_type = nal_unit_type ;
rtph265depay - > fu_timestamp = timestamp ;
rtph265depay - > wait_start = FALSE ;
/* reconstruct NAL header */
nal_header =
( ( payload [ 0 ] & 0x3f ) < < 9 ) | ( nuh_layer_id < < 3 ) |
nuh_temporal_id_plus1 ;
/* go back one byte so we can copy the payload + two bytes more in the front which
* will be overwritten by the nal_header
*/
payload - = 1 ;
payload_len + = 1 ;
nalu_size = payload_len ;
outsize = nalu_size + sizeof ( sync_bytes ) ;
outbuf = gst_buffer_new_and_alloc ( outsize ) ;
gst_buffer_map ( outbuf , & map , GST_MAP_WRITE ) ;
memcpy ( map . data + sizeof ( sync_bytes ) , payload , nalu_size ) ;
map . data [ sizeof ( sync_bytes ) ] = nal_header > > 8 ;
map . data [ sizeof ( sync_bytes ) + 1 ] = nal_header & 0xff ;
gst_buffer_unmap ( outbuf , & map ) ;
2015-08-14 10:49:51 +00:00
gst_rtp_copy_meta ( GST_ELEMENT_CAST ( rtph265depay ) , outbuf , buf ,
g_quark_from_static_string ( GST_META_TAG_VIDEO_STR ) ) ;
2014-10-16 08:34:01 +00:00
GST_DEBUG_OBJECT ( rtph265depay , " queueing %d bytes " , outsize ) ;
/* and assemble in the adapter */
gst_adapter_push ( rtph265depay - > adapter , outbuf ) ;
} else {
GST_DEBUG_OBJECT ( rtph265depay ,
" Following part of Fragmentation Unit " ) ;
/* strip off FU header byte */
payload + = 1 ;
payload_len - = 1 ;
outsize = payload_len ;
outbuf = gst_buffer_new_and_alloc ( outsize ) ;
gst_buffer_fill ( outbuf , 0 , payload , outsize ) ;
2015-08-14 10:49:51 +00:00
gst_rtp_copy_meta ( GST_ELEMENT_CAST ( rtph265depay ) , outbuf , buf ,
g_quark_from_static_string ( GST_META_TAG_VIDEO_STR ) ) ;
2014-10-16 08:34:01 +00:00
GST_DEBUG_OBJECT ( rtph265depay , " queueing %d bytes " , outsize ) ;
/* and assemble in the adapter */
gst_adapter_push ( rtph265depay - > adapter , outbuf ) ;
}
outbuf = NULL ;
rtph265depay - > fu_marker = marker ;
/* if NAL unit ends, flush the adapter */
if ( E ) {
outbuf = gst_rtp_h265_push_fragmentation_unit ( rtph265depay , FALSE ) ;
GST_DEBUG_OBJECT ( rtph265depay , " End of Fragmentation Unit " ) ;
}
break ;
}
case 50 :
goto not_implemented ; /* PACI packets Section 4.9 */
default :
{
rtph265depay - > wait_start = FALSE ;
/* All other cases: Single NAL unit packet Section 4.6 */
/* the entire payload is the output buffer */
2015-03-06 14:54:41 +00:00
#if 0
2014-10-16 08:34:01 +00:00
if ( donl_present )
goto not_implemented_donl_present ;
2015-03-06 14:54:41 +00:00
# endif
2014-10-16 08:34:01 +00:00
nalu_size = payload_len ;
outsize = nalu_size + sizeof ( sync_bytes ) ;
outbuf = gst_buffer_new_and_alloc ( outsize ) ;
gst_buffer_map ( outbuf , & map , GST_MAP_WRITE ) ;
if ( rtph265depay - > byte_stream ) {
memcpy ( map . data , sync_bytes , sizeof ( sync_bytes ) ) ;
} else {
goto not_implemented ;
}
memcpy ( map . data + sizeof ( sync_bytes ) , payload , nalu_size ) ;
gst_buffer_unmap ( outbuf , & map ) ;
2015-08-14 10:49:51 +00:00
gst_rtp_copy_meta ( GST_ELEMENT_CAST ( rtph265depay ) , outbuf , buf ,
g_quark_from_static_string ( GST_META_TAG_VIDEO_STR ) ) ;
2014-10-16 08:34:01 +00:00
outbuf = gst_rtp_h265_depay_handle_nal ( rtph265depay , outbuf , timestamp ,
marker ) ;
break ;
}
}
}
2015-08-14 10:49:51 +00:00
gst_buffer_unref ( buf ) ;
2014-10-16 08:34:01 +00:00
return outbuf ;
/* ERRORS */
empty_packet :
{
GST_DEBUG_OBJECT ( rtph265depay , " empty packet " ) ;
2015-08-14 10:49:51 +00:00
gst_buffer_unref ( buf ) ;
2014-10-16 08:34:01 +00:00
return NULL ;
}
waiting_start :
{
GST_DEBUG_OBJECT ( rtph265depay , " waiting for start " ) ;
2015-08-14 10:49:51 +00:00
gst_buffer_unref ( buf ) ;
2014-10-16 08:34:01 +00:00
return NULL ;
}
2015-03-06 14:54:41 +00:00
#if 0
2014-10-16 08:34:01 +00:00
not_implemented_donl_present :
{
GST_ELEMENT_ERROR ( rtph265depay , STREAM , FORMAT ,
( NULL ) , ( " DONL field present not supported yet " ) ) ;
2015-08-14 10:49:51 +00:00
gst_buffer_unref ( buf ) ;
2014-10-16 08:34:01 +00:00
return NULL ;
}
2015-03-06 14:54:41 +00:00
# endif
2014-10-16 08:34:01 +00:00
not_implemented :
{
GST_ELEMENT_ERROR ( rtph265depay , STREAM , FORMAT ,
( NULL ) , ( " NAL unit type %d not supported yet " , nal_unit_type ) ) ;
2015-08-14 10:49:51 +00:00
gst_buffer_unref ( buf ) ;
2014-10-16 08:34:01 +00:00
return NULL ;
}
}
static gboolean
gst_rtp_h265_depay_handle_event ( GstRTPBaseDepayload * depay , GstEvent * event )
{
GstRtpH265Depay * rtph265depay ;
rtph265depay = GST_RTP_H265_DEPAY ( depay ) ;
switch ( GST_EVENT_TYPE ( event ) ) {
case GST_EVENT_FLUSH_STOP :
gst_rtp_h265_depay_reset ( rtph265depay ) ;
break ;
default :
break ;
}
return
GST_RTP_BASE_DEPAYLOAD_CLASS ( parent_class ) - > handle_event ( depay , event ) ;
}
static GstStateChangeReturn
gst_rtp_h265_depay_change_state ( GstElement * element ,
GstStateChange transition )
{
GstRtpH265Depay * rtph265depay ;
GstStateChangeReturn ret ;
rtph265depay = GST_RTP_H265_DEPAY ( element ) ;
switch ( transition ) {
case GST_STATE_CHANGE_NULL_TO_READY :
break ;
case GST_STATE_CHANGE_READY_TO_PAUSED :
gst_rtp_h265_depay_reset ( rtph265depay ) ;
break ;
default :
break ;
}
ret = GST_ELEMENT_CLASS ( parent_class ) - > change_state ( element , transition ) ;
switch ( transition ) {
case GST_STATE_CHANGE_READY_TO_NULL :
break ;
default :
break ;
}
return ret ;
}
gboolean
gst_rtp_h265_depay_plugin_init ( GstPlugin * plugin )
{
GST_DEBUG_CATEGORY_INIT ( rtph265depay_debug , " rtph265depay " , 0 ,
" H265 Video RTP Depayloader " ) ;
return gst_element_register ( plugin , " rtph265depay " ,
GST_RANK_SECONDARY , GST_TYPE_RTP_H265_DEPAY ) ;
}