[vlc-commits] [Git][videolan/vlc][master] access: rtp: add h264 payload parser module

Rémi Denis-Courmont (@Courmisch) gitlab at videolan.org
Fri Jan 28 14:49:41 UTC 2022



Rémi Denis-Courmont pushed to branch master at VideoLAN / VLC


Commits:
a1a4763d by Francois Cartegnie at 2022-01-28T15:14:25+01:00
access: rtp: add h264 payload parser module

- - - - -


2 changed files:

- modules/access/rtp/Makefile.am
- + modules/access/rtp/h264.c


Changes:

=====================================
modules/access/rtp/Makefile.am
=====================================
@@ -34,6 +34,7 @@ rtpparse_LTLIBRARIES = \
 	librtp_ac3_plugin.la \
 	librtp_mpeg12_plugin.la \
 	librtp_pcm_plugin.la \
+	librtp_h264_plugin.la \
 	librtp_xiph_plugin.la
 
 librtp_ac3_plugin_la_SOURCES = access/rtp/ac3.c
@@ -42,4 +43,6 @@ librtp_mpeg12_plugin_la_SOURCES = access/rtp/mpeg12.c
 librtp_pcm_plugin_la_SOURCES = access/rtp/pcm.c
 librtp_pcm_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/access/rtp
 
+librtp_h264_plugin_la_SOURCES = access/rtp/h264.c
+
 librtp_xiph_plugin_la_SOURCES = access/rtp/xiph.c


=====================================
modules/access/rtp/h264.c
=====================================
@@ -0,0 +1,396 @@
+/**
+ * @file h264.c
+ */
+/*****************************************************************************
+ * Copyright (C) 2021 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 <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_block.h>
+#include <vlc_strings.h>
+#include <vlc_codec.h>
+
+#include "rtp.h"
+#include "sdp.h"
+
+struct rtp_h26x_sys
+{
+    vlc_tick_t pts;
+    block_t **pp_packets_next;
+    block_t *p_packets;
+    block_t *xps;
+    struct vlc_rtp_es *es;
+};
+
+static void rtp_h26x_clear(struct rtp_h26x_sys *sys)
+{
+    block_ChainRelease(sys->p_packets);
+    if(sys->xps)
+        block_Release(sys->xps);
+}
+
+static void rtp_h26x_init(struct rtp_h26x_sys *sys)
+{
+    sys->pts = VLC_TICK_INVALID;
+    sys->p_packets = NULL;
+    sys->pp_packets_next = &sys->p_packets;
+    sys->xps = NULL;
+    sys->es = NULL;
+}
+
+static const uint8_t annexbheader[] = { 0, 0, 0, 1 };
+
+static block_t * h26x_wrap_prefix(block_t *block, bool b_annexb)
+{
+    block = block_Realloc(block, 4, block->i_buffer);
+    if(block)
+    {
+        if(b_annexb)
+            memcpy(block->p_buffer, annexbheader, 4);
+        else
+            SetDWBE(block->p_buffer, block->i_buffer - 4);
+    }
+    return block;
+}
+
+static void h26x_extractbase64xps(const char *psz64,
+                                  const char *pszend,
+                                  void(*pf_output)(void *, uint8_t *, size_t),
+                                  void *outputsys)
+{
+    do
+    {
+        psz64 += strspn(psz64, " ");
+        uint8_t *xps = NULL;
+        size_t xpssz = vlc_b64_decode_binary(&xps, psz64);
+        pf_output(outputsys, xps, xpssz);
+        psz64 = strchr(psz64, ',');
+        if(psz64)
+            ++psz64;
+    } while(psz64 && *psz64 && psz64 < pszend);
+}
+
+static void h264_add_xps(void *priv, uint8_t *xps, size_t xpssz)
+{
+    block_t *b = block_heap_Alloc(xps, xpssz);
+    if(!b || !(b = h26x_wrap_prefix(b, true)))
+        return;
+
+    block_t ***ppp_append = priv;
+    **ppp_append = b;
+    *ppp_append = &((**ppp_append)->p_next);
+}
+
+static block_t * h264_fillextradata (const char *psz)
+{
+    block_t *xps = NULL;
+    block_t **pxps = &xps;
+    h26x_extractbase64xps(psz, strchr(psz, ';'), h264_add_xps, &pxps);
+    if(xps)
+        xps = block_ChainGather(xps);
+    return xps;
+}
+
+static void *rtp_h264_init(struct vlc_rtp_pt *pt)
+{
+    block_t *sdpparams = 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_H264);
+    fmt.b_packetized = false;
+
+    sys->es = vlc_rtp_pt_request_es(pt, &fmt);
+    if(sdpparams)
+        sys->xps = block_Duplicate(sdpparams);
+
+    return sys;
+}
+
+static void rtp_h264_destroy(struct vlc_rtp_pt *pt, void *data)
+{
+    VLC_UNUSED(pt);
+    struct rtp_h26x_sys *sys = data;
+    if(sys)
+    {
+        vlc_rtp_es_destroy(sys->es);
+        rtp_h26x_clear(sys);
+        free(sys);
+    }
+}
+
+static block_t * h264_deaggregate_STAP(block_t *block, bool b_annexb)
+{
+    size_t total = 0;
+    /* first pass, compute final size */
+    const uint8_t *p = block->p_buffer;
+    size_t sz = block->i_buffer;
+    while(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 > 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;
+    }
+    return block;
+}
+
+static block_t * h264_chainsplit_MTAP(block_t *block, bool b_24ext,
+                                      bool b_annexb)
+{
+    const uint8_t *p = block->p_buffer;
+    size_t sz = block->i_buffer;
+    const uint8_t tssz = b_24ext ? 3 : 2;
+    block_t *p_chain = NULL, **pp_chain_append = &p_chain;
+    if(sz > 3)
+    {
+        /* Skip Header and DONB */
+        p += 3;
+        sz -= 3;
+        /* Sz, DOND, TS Offset 16/24 */
+        const uint8_t payloadhdrsz = 2 + 1 + tssz;
+        while(sz > payloadhdrsz)
+        {
+            size_t nalsz = GetWBE(p);
+            if(payloadhdrsz + nalsz > sz)
+                break;
+            uint32_t tsoffset = 0;
+            for(uint8_t i=0; i< tssz; i++)
+                tsoffset = (tsoffset << 8) | p[2 + 1 + i];
+            block_t *out = block_Alloc(nalsz + 4);
+            if(out)
+            {
+                memcpy(&out->p_buffer[4], &p[payloadhdrsz], nalsz);
+                if(b_annexb)
+                    memcpy(out->p_buffer, annexbheader, 4);
+                else
+                    SetDWBE(out->p_buffer, nalsz);
+                /* store offset as length */
+                out->i_pts = block->i_pts + vlc_tick_from_samples(tsoffset, 90000);
+                block_ChainLastAppend(&pp_chain_append, out);
+            }
+            p += payloadhdrsz + nalsz;
+            sz -= payloadhdrsz + nalsz;
+        }
+    }
+    block_Release(block);
+    return p_chain;
+}
+
+static void h26x_output(struct rtp_h26x_sys *sys,
+                        block_t *block,
+                        vlc_tick_t pts, bool pcr, bool au_end)
+{
+//    if(pcr)
+//        es_out_SetPCR(out, pts);
+
+    if(!block)
+        return;
+
+    if(sys->xps)
+    {
+        block_t *xps = sys->xps;
+        sys->xps = NULL;
+        h26x_output(sys, xps, pts, pcr, false);
+    }
+
+    block->i_pts = pts;
+    block->i_dts = VLC_TICK_INVALID; /* RTP does not specify this */
+    if(au_end)
+        block->i_flags |= BLOCK_FLAG_AU_END;
+    vlc_rtp_es_send(sys->es, block);
+}
+
+static void h26x_output_blocks(struct rtp_h26x_sys *sys, bool b_annexb)
+{
+    if(!sys->p_packets)
+        return;
+    block_t *out = block_ChainGather(sys->p_packets);
+    sys->p_packets = NULL;
+    sys->pp_packets_next = &sys->p_packets;
+    out = h26x_wrap_prefix(out, b_annexb);
+    h26x_output(sys, out, sys->pts, true, false);
+}
+
+static void rtp_h264_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)
+        goto drop;
+
+    const uint8_t type = block->p_buffer[0] & 0x1F;
+    const vlc_tick_t pts = block->i_pts;
+    switch(type)
+    {
+        case 24: /* STAP-A */
+            /* end unfinished aggregates */
+            h26x_output_blocks(sys, true);
+            /* skip header */
+            block->i_buffer -= 1;
+            block->p_buffer += 1;
+            block = h264_deaggregate_STAP(block, true);
+            h26x_output(sys, block, pts, true, b_au_end);
+            break;
+        case 25: /* STAP-B */
+            /* TODO */
+            goto drop;
+        case 26: /* MTAP16 */
+        case 27:
+        {
+            block = h264_chainsplit_MTAP(block, type == 27, true);
+            /* result is a chain */
+            while(block)
+            {
+                block_t *p_next = block->p_next;
+                block->p_next = NULL;
+                h26x_output(sys, block, block->i_pts, true, b_au_end && !p_next);
+                block = p_next;
+            }
+            break;
+        }
+        case 28: /* FU-A */
+        case 29: /* FU-B */
+        {
+            if(block->i_buffer < 3)
+                goto drop;
+            const bool start = block->p_buffer[1] & 0x80;
+            const bool end = block->p_buffer[1] & 0x40;
+            const uint8_t naltype = block->p_buffer[1] & 0x1f;
+            /* skip FU header and rebuild NAL header */
+            if(start)
+            {
+                /* end unfinished aggregates */
+                h26x_output_blocks(sys, true);
+                /* rebuild NAL header */
+                block->p_buffer[1] = (block->p_buffer[0] & 0xE0) | naltype;
+                block->i_buffer -= 1;
+                block->p_buffer += 1;
+                sys->pts = pts;
+            }
+            else /* trail data */
+            {
+                block->i_buffer -= 2;
+                block->p_buffer += 2;
+            }
+            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, sys->pts, true, b_au_end);
+            }
+            break;
+        }
+        default:
+            /* end unfinished aggregates */
+            h26x_output_blocks(sys, true);
+            if(type > 0 && type < 24) /* raw single NAL */
+            {
+                block = h26x_wrap_prefix(block, true);
+                h26x_output(sys, block, pts, true, b_au_end);
+            }
+            else goto drop;
+    }
+    return;
+
+drop:
+    block_Release(block);
+}
+
+static void rtp_h264_release(struct vlc_rtp_pt *pt)
+{
+    block_t *sdpparams = pt->opaque;
+    if(sdpparams)
+        block_Release(sdpparams);
+}
+
+static const struct vlc_rtp_pt_operations rtp_h264_ops = {
+    rtp_h264_release, rtp_h264_init, rtp_h264_destroy, rtp_h264_decode,
+};
+
+static int rtp_h264_open(vlc_object_t *obj, struct vlc_rtp_pt *pt,
+                         const struct vlc_sdp_pt *desc)
+{
+    VLC_UNUSED(obj);
+
+    const char *psz = strstr(desc->parameters, "packetization-mode=");
+    if(!psz || psz[19] == '\0' || atoi(&psz[19]) > 1)
+        return VLC_ENOTSUP;
+
+    if (vlc_ascii_strcasecmp(desc->name, "H264") == 0)
+        pt->ops = &rtp_h264_ops;
+    else
+        return VLC_ENOTSUP;
+
+    if(desc->parameters)
+    {
+        psz = strstr(desc->parameters, "sprop-parameter-sets=");
+        if(psz)
+            pt->opaque = h264_fillextradata(psz + 21);
+    }
+
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+    set_shortname(N_("RTP H264"))
+    set_description(N_("RTP H264 payload parser"))
+    set_subcategory(SUBCAT_INPUT_DEMUX)
+    set_rtp_parser_callback(rtp_h264_open)
+    add_shortcut("video/H264")
+vlc_module_end()



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/a1a4763d50a33a69c5ee56b3f9bf68b761eaa502

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/a1a4763d50a33a69c5ee56b3f9bf68b761eaa502
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list