[vlc-commits] [Git][videolan/vlc][master] demux: add HXVS/HXVT

François Cartegnie (@fcartegnie) gitlab at videolan.org
Wed Jan 19 20:12:05 UTC 2022



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
c6436c80 by Francois Cartegnie at 2022-01-19T19:04:57+00:00
demux: add HXVS/HXVT

Custom .265 .264 files mux from some IP cameras

refs #22935
streams.videolan.org/samples/HX/

- - - - -


2 changed files:

- modules/demux/Makefile.am
- + modules/demux/hx.c


Changes:

=====================================
modules/demux/Makefile.am
=====================================
@@ -45,6 +45,9 @@ demux_LTLIBRARIES += libwav_plugin.la
 libnsv_plugin_la_SOURCES = demux/nsv.c
 demux_LTLIBRARIES += libnsv_plugin.la
 
+libhx_plugin_la_SOURCES = demux/hx.c
+demux_LTLIBRARIES += libhx_plugin.la
+
 libps_plugin_la_SOURCES = demux/mpeg/ps.c demux/mpeg/ps.h demux/mpeg/pes.h
 demux_LTLIBRARIES += libps_plugin.la
 


=====================================
modules/demux/hx.c
=====================================
@@ -0,0 +1,385 @@
+/*****************************************************************************
+ * hx.c : raw video / audio IP cam demuxer
+ *****************************************************************************
+ * Copyright (C) 2022 - VideoLabs, VLC authors and VideoLAN
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_demux.h>
+
+/* All Marker formats:
+ * [tag.4][size.4][tag dependant.8] */
+static const char HXVS[4] = { 'H', 'X', 'V', 'S' }; /* H264 file tag */
+static const char HXVT[4] = { 'H', 'X', 'V', 'T' }; /* HEVC */
+static const char HXVF[4] = { 'H', 'X', 'V', 'F' }; /* Video samples */
+static const char HXAF[4] = { 'H', 'X', 'A', 'F' }; /* Audio samples */
+static const char HXFI[4] = { 'H', 'X', 'F', 'I' }; /* RAP Index */
+
+#define HX_HEADER_SIZE 16
+#define HX_INDEX_SIZE 200000
+
+struct hxfi_index
+{
+    uint32_t offset;
+    uint32_t time;
+};
+
+typedef struct
+{
+    es_out_id_t *p_es_video;
+    es_out_id_t *p_es_audio;
+    vlc_tick_t video_pts;
+    vlc_tick_t audio_pts;
+    vlc_tick_t pcr;
+    uint32_t video_pts_offset;
+    uint32_t audio_pts_offset;
+    uint32_t duration;
+    DECL_ARRAY(struct hxfi_index) index;
+} hx_sys_t;
+
+static int LoadIndex(demux_t *p_demux)
+{
+    hx_sys_t *p_sys  = p_demux->p_sys;
+
+    /* From what I understand:
+     * Header HXFI[size.4][duration.4][?.4]
+     * Size is always 200K, predictable file offset
+     * All indexes entries are 4/4 bytes
+     * zero filled empty entries
+     */
+
+    uint64_t size;
+    if(vlc_stream_GetSize(p_demux->s, &size) != VLC_SUCCESS ||
+       size < HX_INDEX_SIZE + HX_HEADER_SIZE)
+        return VLC_EGENERIC;
+
+    if(vlc_stream_Seek(p_demux->s, size - HX_INDEX_SIZE - HX_HEADER_SIZE) != VLC_SUCCESS)
+        return VLC_EGENERIC;
+
+    uint8_t temp[HX_HEADER_SIZE];
+    if(vlc_stream_Read(p_demux->s, temp, HX_HEADER_SIZE) != HX_HEADER_SIZE ||
+       memcmp(temp, HXFI, 4))
+    {
+        msg_Warn(p_demux, "Could not find index at expected location");
+        return VLC_EGENERIC;
+    }
+
+    p_sys->duration = GetDWLE(temp + 8);
+    msg_Dbg(p_demux, "Reading Index Length @%" PRIu32 "ms", p_sys->duration);
+
+    uint32_t prevtime = UINT32_MAX;
+    for(;;)
+    {
+        if(vlc_stream_Read(p_demux->s, temp, 8) != 8)
+            break;
+        struct hxfi_index entry;
+        entry.offset = GetDWLE(temp);
+        entry.time = GetDWLE(temp + 4);
+        if(entry.offset == 0)
+            break;
+        if(entry.time != prevtime)
+        {
+            prevtime = entry.time;
+            ARRAY_APPEND(p_sys->index, entry);
+        }
+    }
+    msg_Dbg(p_demux, "Using %d entries from HXFI index", p_sys->index.i_size);
+    return VLC_SUCCESS;
+}
+
+static struct hxfi_index LookupIndex(hx_sys_t *p_sys, uint32_t time)
+{
+    struct hxfi_index entry = { 0, 0 };
+    if(p_sys->index.i_size)
+    {
+        unsigned l = 0, h = p_sys->index.i_size - 1;
+        while(l <= h)
+        {
+            unsigned m = (l + h) >> 1;
+            if(p_sys->index.p_elems[m].time > time)
+            {
+                if(m == 0)
+                    break;
+                h = m -1;
+            }
+            else
+            {
+                /* store lowest as temp result */
+                entry = p_sys->index.p_elems[m];
+                if(entry.time == time)
+                    break;
+                l = m + 1;
+            }
+        };
+    }
+    return entry;
+}
+
+static int Demux(demux_t *p_demux)
+{
+    hx_sys_t *p_sys  = p_demux->p_sys;
+
+    uint8_t header[HX_HEADER_SIZE];
+    if(vlc_stream_Read(p_demux->s, header, HX_HEADER_SIZE) != HX_HEADER_SIZE)
+        return VLC_DEMUXER_EOF;
+
+    es_out_id_t *es = NULL;
+    vlc_tick_t *ppts;
+    uint32_t *ppts_offset;
+
+    if(!memcmp(header, HXVF, 4))
+    {
+        es = p_sys->p_es_video;
+        ppts = &p_sys->video_pts;
+        ppts_offset = &p_sys->video_pts_offset;
+    }
+    else if(!memcmp(header, HXAF, 4))
+    {
+        es = p_sys->p_es_audio;
+        ppts = &p_sys->audio_pts;
+        ppts_offset = &p_sys->audio_pts_offset;
+    }
+    else if(!memcmp(header, HXVS, 4) || !memcmp(header, HXVT, 4))
+    {
+        return VLC_DEMUXER_SUCCESS;
+    }
+    else
+    {
+        msg_Dbg(p_demux, "EOF on %4.4s", (const char *) header);
+        return VLC_DEMUXER_EOF;
+    }
+
+    uint32_t sz = GetDWLE(header + 4);
+    int i_ret = VLC_DEMUXER_SUCCESS;
+
+    if(es)
+    {
+        /* [HXAF.4][size.4][pts.4][?.4] */
+        /* [HXVF.4][size.4][pts.4][?.4] */
+        *ppts = VLC_TICK_0;
+        if(*ppts_offset != UINT32_MAX)
+            *ppts += VLC_TICK_FROM_MS(GetDWLE(&header[8]) - *ppts_offset);
+        else
+            *ppts_offset = GetDWLE(&header[8]);
+
+        block_t *p_block = vlc_stream_Block(p_demux->s, sz);
+        if(p_block == NULL)
+            return VLC_DEMUXER_EOF;
+
+        if(p_block->i_buffer < sz)
+            i_ret = VLC_DEMUXER_EOF;
+
+        if(p_sys->p_es_audio == es)
+        {
+            /* HXAF sample format prefix [?.1/channels.1/rate.1/?.1] */
+            const uint8_t audioprefix[4] = {0x00, 0x01, 0x50, 0x00};
+            if(p_block->i_buffer >= 4 &&
+               !memcmp(p_block->p_buffer, audioprefix, 4))
+            {
+                p_block->i_buffer -= 4;
+                p_block->p_buffer += 4;
+            }
+            else
+            {
+                msg_Warn(p_demux,"Unsupported audio format, dropping");
+                block_Release(p_block);
+                p_block = NULL;
+            }
+        }
+
+        if(p_sys->pcr == VLC_TICK_INVALID)
+        {
+            p_sys->pcr = *ppts;
+            es_out_SetPCR(p_demux->out, p_sys->pcr);
+        }
+
+        if(p_block)
+        {
+            p_block->i_dts = p_block->i_pts = *ppts;
+            es_out_Send(p_demux->out, es, p_block);
+        }
+
+        if( p_sys->audio_pts > p_sys->pcr )
+        {
+            p_sys->pcr = p_sys->audio_pts;
+            es_out_SetPCR(p_demux->out, p_sys->pcr);
+        }
+    }
+    else
+    {
+        if(vlc_stream_Read(p_demux->s, NULL, sz) != sz)
+            i_ret = VLC_DEMUXER_EOF;
+    }
+
+    return i_ret;
+}
+
+static int SeekUsingIndex(demux_t *p_demux, uint32_t time, bool b_precise)
+{
+    hx_sys_t *p_sys  = p_demux->p_sys;
+    struct hxfi_index idx = LookupIndex(p_sys, time);
+    if(idx.offset == 0)
+        return VLC_EGENERIC;
+    int ret = vlc_stream_Seek(p_demux->s, idx.offset);
+    if(ret == VLC_SUCCESS)
+    {
+        p_sys->pcr = VLC_TICK_0 + VLC_TICK_FROM_MS(idx.time);
+        p_sys->audio_pts = VLC_TICK_INVALID;
+        p_sys->video_pts = VLC_TICK_INVALID;
+        if(b_precise)
+            es_out_Control(p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
+                           VLC_TICK_0 + VLC_TICK_FROM_MS(time));
+    }
+    return ret;
+}
+
+static int Control(demux_t *p_demux, int i_query, va_list args)
+{
+    hx_sys_t *p_sys  = p_demux->p_sys;
+    switch(i_query)
+    {
+        case DEMUX_GET_TIME:
+        {
+            *va_arg(args, vlc_tick_t *) = p_sys->pcr;
+            return VLC_SUCCESS;
+        }
+        case DEMUX_GET_LENGTH:
+        {
+            *va_arg(args, vlc_tick_t *) = VLC_TICK_FROM_MS(p_sys->duration);
+            return VLC_SUCCESS;
+        }
+        case DEMUX_GET_POSITION:
+        {
+            if(!p_sys->duration)
+                return VLC_EGENERIC;
+            *va_arg(args, double *) = MS_FROM_VLC_TICK(p_sys->pcr) / p_sys->duration;
+            return VLC_SUCCESS;
+        }
+        case DEMUX_SET_TIME:
+        {
+            vlc_tick_t time = va_arg(args, vlc_tick_t);
+            bool b_precise = va_arg(args, int);
+            return SeekUsingIndex(p_demux, MS_FROM_VLC_TICK(time), b_precise);
+        }
+        case DEMUX_SET_POSITION:
+        {
+            double pos = va_arg(args, double);
+            bool b_precise = va_arg(args, int);
+            if(p_sys->index.i_size == 0)
+                return VLC_EGENERIC;
+            return SeekUsingIndex(p_demux, pos * p_sys->duration, b_precise);
+        }
+        case DEMUX_CAN_SEEK:
+        {
+            if(p_sys->index.i_size == 0)
+                return VLC_EGENERIC;
+            /* fallthrough */
+        }
+        default:
+            break;
+    }
+
+    return demux_vaControlHelper(p_demux->s, 0, -1, 0, 1, i_query, args);
+}
+
+static void Close(vlc_object_t *p_this)
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    hx_sys_t *p_sys  = p_demux->p_sys;
+    ARRAY_RESET(p_sys->index);
+    free(p_sys);
+}
+
+static int Open(vlc_object_t * p_this)
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    hx_sys_t *p_sys;
+    const uint8_t *p_peek;
+
+    if(vlc_stream_Peek(p_demux->s, &p_peek, 20) != 20 ||
+       (memcmp(p_peek, HXVS, 4) && memcmp(p_peek, HXVT, 4)) ||
+        memcmp(p_peek + 16, HXVF, 4))
+        return VLC_EGENERIC;
+
+    p_demux->p_sys = p_sys = malloc(sizeof(hx_sys_t));
+    if(!p_sys)
+        return VLC_ENOMEM;
+    p_sys->audio_pts = VLC_TICK_INVALID;
+    p_sys->video_pts = VLC_TICK_INVALID;
+    p_sys->pcr = VLC_TICK_INVALID;
+    p_sys->audio_pts_offset = UINT32_MAX;
+    p_sys->video_pts_offset = UINT32_MAX;
+    p_sys->p_es_audio = NULL;
+    p_sys->p_es_video = NULL;
+    p_sys->duration = 0;
+    ARRAY_INIT(p_sys->index);
+
+    bool b_seekable;
+    if(vlc_stream_Control(p_demux->s, STREAM_CAN_SEEK, &b_seekable) != VLC_SUCCESS)
+        b_seekable = false;
+
+    vlc_fourcc_t vcodec = memcmp(p_peek, HXVT, 4) ? VLC_CODEC_H264 : VLC_CODEC_HEVC;
+    unsigned width = GetDWLE(p_peek + 4);
+    unsigned height = GetDWLE(p_peek + 8);
+
+    if(b_seekable)
+    {
+        uint64_t origin = vlc_stream_Tell(p_demux->s);
+        if(LoadIndex(p_demux) != VLC_SUCCESS)
+            msg_Err(p_demux, "Failed to load index");
+        if(vlc_stream_Seek(p_demux->s, origin) != VLC_SUCCESS)
+        {
+            msg_Err(p_demux, "Failed to seek after loading index, giving up");
+            Close(p_this);
+            return VLC_EGENERIC;
+        }
+    }
+
+    /* Create ES from parameters or defaults */
+    es_format_t fmt;
+
+    es_format_Init(&fmt, VIDEO_ES, vcodec);
+    fmt.video.i_visible_width = width;
+    fmt.video.i_visible_height = height;
+    fmt.b_packetized = false;
+    p_sys->p_es_video = es_out_Add(p_demux->out, &fmt);
+    es_format_Clean(&fmt);
+
+    es_format_Init(&fmt, AUDIO_ES, VLC_CODEC_ALAW);
+    fmt.audio.i_rate = 8000;
+    fmt.audio.i_channels = 1;
+    p_sys->p_es_audio = es_out_Add(p_demux->out, &fmt);
+    es_format_Clean(&fmt);
+
+    p_demux->pf_demux   = Demux;
+    p_demux->pf_control = Control;
+
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin ()
+    set_shortname("HX")
+    set_description(N_("HX video demuxer"))
+    set_capability("demux", 10)
+    set_subcategory(SUBCAT_INPUT_DEMUX)
+    set_callbacks(Open, Close)
+    add_shortcut("hx")
+vlc_module_end ()



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

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




More information about the vlc-commits mailing list