You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
9.7 KiB
327 lines
9.7 KiB
/**
|
|
* @file h265.c
|
|
*/
|
|
/*****************************************************************************
|
|
* Copyright (C) 2022 VideoLabs, VLC authors and VideoLAN
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser 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 <assert.h>
|
|
|
|
#include "h26x.h"
|
|
#include "fmtp.h"
|
|
|
|
#define FLAG_DONL 1
|
|
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_codec.h>
|
|
|
|
struct h265_pt_opaque
|
|
{
|
|
block_t *sdpxps;
|
|
bool b_donl;
|
|
vlc_object_t *obj;
|
|
};
|
|
|
|
static void *rtp_h265_init(struct vlc_rtp_pt *pt)
|
|
{
|
|
struct h265_pt_opaque *opaque = pt->opaque;
|
|
struct rtp_h26x_sys *sys = malloc(sizeof(*sys));
|
|
if(!sys)
|
|
return NULL;
|
|
rtp_h26x_init(sys);
|
|
|
|
es_format_t fmt;
|
|
es_format_Init (&fmt, VIDEO_ES, VLC_CODEC_HEVC);
|
|
fmt.b_packetized = false;
|
|
|
|
sys->p_packetizer = demux_PacketizerNew(opaque->obj, &fmt, "rtp packetizer");
|
|
if(!sys->p_packetizer)
|
|
{
|
|
free(sys);
|
|
return NULL;
|
|
}
|
|
|
|
sys->es = vlc_rtp_pt_request_es(pt, &sys->p_packetizer->fmt_out);
|
|
if(opaque->sdpxps)
|
|
sys->xps = block_Duplicate(opaque->sdpxps);
|
|
if(opaque->b_donl)
|
|
sys->flags = FLAG_DONL;
|
|
|
|
return sys;
|
|
}
|
|
|
|
static void rtp_h265_destroy(struct vlc_rtp_pt *pt, void *data)
|
|
{
|
|
VLC_UNUSED(pt);
|
|
struct rtp_h26x_sys *sys = data;
|
|
if(sys)
|
|
{
|
|
if(sys->p_packetizer)
|
|
demux_PacketizerDestroy(sys->p_packetizer);
|
|
vlc_rtp_es_destroy(sys->es);
|
|
rtp_h26x_clear(sys);
|
|
free(sys);
|
|
}
|
|
}
|
|
|
|
static block_t * h265_deaggregate_AP(block_t *block, bool b_donl, bool b_annexb)
|
|
{
|
|
size_t total = 0;
|
|
|
|
if(b_donl)
|
|
{
|
|
if(block->i_buffer < 4)
|
|
{
|
|
block_Release(block);
|
|
return NULL;
|
|
}
|
|
/* Skip Half DONL, so we always get an extra 1 byte extra prefix */
|
|
block->p_buffer += 1;
|
|
block->i_buffer -= 1;
|
|
}
|
|
|
|
const uint8_t *p = block->p_buffer;
|
|
size_t sz = block->i_buffer;
|
|
|
|
/* first pass, compute final size */
|
|
while(sz > (b_donl ? 3 : 2))
|
|
{
|
|
/* skip 1/2 DONL or DOND here */
|
|
if(b_donl)
|
|
{
|
|
p += 1;
|
|
sz -= 1;
|
|
}
|
|
size_t nalsz = GetWBE(p);
|
|
if(nalsz + 2 > sz)
|
|
break;
|
|
total += nalsz + 4;
|
|
sz -= (nalsz + 2);
|
|
p += (nalsz + 2);
|
|
}
|
|
block_t *newblock = block_Alloc(total);
|
|
if(newblock)
|
|
{
|
|
uint8_t *dst = newblock->p_buffer;
|
|
p = block->p_buffer;
|
|
sz = block->i_buffer;
|
|
while(sz > (b_donl ? 3 : 2))
|
|
{
|
|
/* skip 1/2 DONL or DOND here */
|
|
if(b_donl)
|
|
{
|
|
p += 1;
|
|
sz -= 1;
|
|
}
|
|
size_t nalsz = GetWBE(p);
|
|
if(nalsz + 2 > sz)
|
|
break;
|
|
if(b_annexb)
|
|
memcpy(dst, annexbheader, 4);
|
|
else
|
|
SetDWBE(dst, nalsz);
|
|
dst += 4;
|
|
memcpy(dst, p + 2, nalsz);
|
|
dst += nalsz;
|
|
sz -= (nalsz + 2);
|
|
p += (nalsz + 2);
|
|
}
|
|
block_Release(block);
|
|
block = newblock;
|
|
}
|
|
else
|
|
{
|
|
block_Release(block);
|
|
block = NULL;
|
|
}
|
|
return block;
|
|
}
|
|
|
|
static void rtp_h265_decode (struct vlc_rtp_pt *pt, void *data, block_t *block,
|
|
const struct vlc_rtp_pktinfo *restrict info)
|
|
{
|
|
VLC_UNUSED(pt);
|
|
struct rtp_h26x_sys *sys = data;
|
|
const bool b_au_end = info->m;
|
|
|
|
if(block->i_buffer < 2)
|
|
{
|
|
block_Release(block);
|
|
return;
|
|
}
|
|
|
|
const uint8_t type = (block->p_buffer[0] & 0x7E) >> 1;
|
|
const uint8_t layerID = ((block->p_buffer[0] & 0x01) << 5) | (block->p_buffer[1] >> 3);
|
|
const uint8_t temporalID = block->p_buffer[1] & 0x07;
|
|
const vlc_tick_t pts = block->i_pts;
|
|
|
|
switch(type)
|
|
{
|
|
case 48: /* AP */
|
|
/* end unfinished aggregates */
|
|
h26x_output_blocks(sys, true);
|
|
/* skip header */
|
|
block->i_buffer -= 2;
|
|
block->p_buffer += 2;
|
|
block = h265_deaggregate_AP(block, sys->flags & FLAG_DONL, true);
|
|
h26x_output(sys, block, pts, true, b_au_end);
|
|
break;
|
|
case 49: /* FU */
|
|
{
|
|
if(block->i_buffer < ((sys->flags & FLAG_DONL) ? 6 : 4))
|
|
goto drop;
|
|
const bool start = block->p_buffer[2] & 0x80;
|
|
const bool end = block->p_buffer[2] & 0x40;
|
|
const uint8_t futype = block->p_buffer[2] & 0x3f;
|
|
|
|
/* skip FU header and rebuild NAL header */
|
|
if(start)
|
|
{
|
|
/* end unfinished aggregates */
|
|
h26x_output_blocks(sys, true);
|
|
/* rebuild NAL header, overwriting last PAYL[DON]FU 2 bytes */
|
|
if(sys->flags & FLAG_DONL)
|
|
{
|
|
block->i_buffer -= 3;
|
|
block->p_buffer += 3;
|
|
}
|
|
else
|
|
{
|
|
block->i_buffer -= 1;
|
|
block->p_buffer += 1;
|
|
}
|
|
block->p_buffer[0] = (futype << 1) | (layerID >> 5);
|
|
block->p_buffer[1] = (layerID << 3) | temporalID;
|
|
sys->pts = pts;
|
|
}
|
|
else /* trail data */
|
|
{
|
|
block->i_buffer -= (sys->flags & FLAG_DONL) ? 5 : 3;
|
|
block->p_buffer += (sys->flags & FLAG_DONL) ? 5 : 3;
|
|
}
|
|
block_ChainLastAppend(&sys->pp_packets_next, block);
|
|
if(end)
|
|
{
|
|
block_t *out = block_ChainGather(sys->p_packets);
|
|
sys->p_packets = NULL;
|
|
sys->pp_packets_next = &sys->p_packets;
|
|
out = h26x_wrap_prefix(out, true);
|
|
h26x_output(sys, out, pts, true, b_au_end);
|
|
}
|
|
break;
|
|
}
|
|
case 50: /* PACI */
|
|
{
|
|
if(block->i_buffer < 5 || (block->p_buffer[3] & 0x01))
|
|
goto drop;
|
|
const uint8_t cType = ((block->p_buffer[2] & 0x7f) >> 1);
|
|
const uint8_t PHSize = ((block->p_buffer[2] & 0x01) << 7)
|
|
| (block->p_buffer[3] >> 4);
|
|
if(PHSize >= block->i_buffer - 4)
|
|
goto drop;
|
|
/* skip extension, and add the payload header */
|
|
block->p_buffer[0] = (block->p_buffer[0] & 0x81) | (cType << 1);
|
|
block->p_buffer[4 + PHSize - 2 + 0] = block->p_buffer[0];
|
|
block->p_buffer[4 + PHSize - 2 + 1] = block->p_buffer[1];
|
|
block->p_buffer += 4 + PHSize;
|
|
block->i_buffer -= 4 + PHSize;
|
|
/* pass to original payload handler */
|
|
rtp_h265_decode(pt, data, block, info);
|
|
break;
|
|
}
|
|
default:
|
|
/* end unfinished aggregates */
|
|
h26x_output_blocks(sys, true);
|
|
/* raw single NAL */
|
|
if(sys->flags & FLAG_DONL) /* optional DONL */
|
|
{
|
|
if(block->i_buffer < 4)
|
|
goto drop;
|
|
/* NALHDR DONL NALPAYL -> NALHDR NALPAYL */
|
|
block->p_buffer[2] = block->p_buffer[0];
|
|
block->p_buffer[3] = block->p_buffer[1];
|
|
block->p_buffer += 2;
|
|
block->i_buffer -= 2;
|
|
}
|
|
block = h26x_wrap_prefix(block, true);
|
|
h26x_output(sys, block, pts, true, b_au_end);
|
|
}
|
|
return;
|
|
|
|
drop:
|
|
if(block)
|
|
block_Release(block);
|
|
}
|
|
|
|
static void rtp_h265_release(struct vlc_rtp_pt *pt)
|
|
{
|
|
struct h265_pt_opaque *opaque = pt->opaque;
|
|
if(opaque->sdpxps)
|
|
block_Release(opaque->sdpxps);
|
|
free(opaque);
|
|
}
|
|
|
|
static const struct vlc_rtp_pt_operations rtp_h265_ops = {
|
|
rtp_h265_release, rtp_h265_init, rtp_h265_destroy, rtp_h265_decode,
|
|
};
|
|
|
|
static int rtp_h265_open(vlc_object_t *obj, struct vlc_rtp_pt *pt,
|
|
const struct vlc_sdp_pt *desc)
|
|
{
|
|
if (vlc_ascii_strcasecmp(desc->name, "H265") == 0)
|
|
pt->ops = &rtp_h265_ops;
|
|
else
|
|
return VLC_ENOTSUP;
|
|
|
|
struct h265_pt_opaque *opaque = calloc(1, sizeof(*opaque));
|
|
if(!opaque)
|
|
return VLC_ENOMEM;
|
|
pt->opaque = opaque;
|
|
|
|
opaque->obj = obj;
|
|
|
|
uint16_t don_diff;
|
|
if(!vlc_sdp_fmtp_get(desc, "sprop-max-don-diff", &don_diff))
|
|
opaque->b_donl = (don_diff > 0);
|
|
|
|
block_t **append = &opaque->sdpxps;
|
|
const char *props[] = { "sprop-vps", "sprop-sps", "sprop-pps" };
|
|
for(size_t i=0; i<ARRAY_SIZE(props); i++) {
|
|
size_t len;
|
|
const char *value = vlc_sdp_fmtp_get_str(desc, props[i], &len);
|
|
if(!value || len == 0)
|
|
continue;
|
|
block_t *xps = h26x_fillextradata(value);
|
|
if(xps)
|
|
block_ChainLastAppend(&append, xps);
|
|
}
|
|
if(opaque->sdpxps)
|
|
opaque->sdpxps = block_ChainGather(opaque->sdpxps);
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
vlc_module_begin()
|
|
set_shortname(N_("RTP H265"))
|
|
set_description(N_("RTP H265 payload parser"))
|
|
set_subcategory(SUBCAT_INPUT_DEMUX)
|
|
set_rtp_parser_callback(rtp_h265_open)
|
|
add_shortcut("video/H265")
|
|
vlc_module_end()
|
|
|