[vlc-commits] [Git][videolan/vlc][master] demux: add support for Genetec G64
Steve Lhomme (@robUx4)
gitlab at videolan.org
Sun Nov 17 09:53:47 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
08630bfb by François Cartegnie at 2024-11-16T17:44:18+00:00
demux: add support for Genetec G64
- - - - -
3 changed files:
- modules/demux/Makefile.am
- + modules/demux/g64rtp.c
- modules/demux/meson.build
Changes:
=====================================
modules/demux/Makefile.am
=====================================
@@ -34,6 +34,9 @@ demux_LTLIBRARIES += librawdv_plugin.la
librawvid_plugin_la_SOURCES = demux/rawvid.c
demux_LTLIBRARIES += librawvid_plugin.la
+libg64rtp_plugin_la_SOURCES = demux/g64rtp.c
+demux_LTLIBRARIES += libg64rtp_plugin.la
+
libau_plugin_la_SOURCES = demux/au.c
demux_LTLIBRARIES += libau_plugin.la
=====================================
modules/demux/g64rtp.c
=====================================
@@ -0,0 +1,936 @@
+/*****************************************************************************
+ * g64rtp.c : G64 raw RTP input module for vlc
+ *****************************************************************************
+ * Copyright (C) 2023 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_demux.h>
+#include <vlc_plugin.h>
+#include <vlc_modules.h>
+#include <vlc_vector.h>
+#include <vlc_charset.h>
+#include <vlc_meta.h>
+
+#include "../access/rtp/rtp.h"
+#include "../access/rtp/sdp.h"
+
+/*****************************************************************************
+ * Definitions of structures used by this plugin
+ *****************************************************************************/
+
+//#define G64_DEBUG
+
+typedef struct
+{
+ vlc_tick_t time;
+ uint64_t offset;
+} index_t;
+
+typedef struct VLC_VECTOR(index_t) vec_index_t;
+
+typedef struct
+{
+ es_out_id_t *es;
+ struct vlc_rtp_es dummy_es;
+ vlc_fourcc_t fcc;
+ uint8_t i_type;
+
+ struct vlc_rtp_pt pt;
+ void *ptpriv;
+
+ vlc_tick_t pcr;
+
+ vec_index_t index;
+ uint64_t i_start_offset;
+
+ vlc_tick_t i_first_pts;
+ vlc_tick_t i_duration;
+ char *psz_title;
+
+} demux_sys_t;
+
+#define G64_WRAP_HEADER_BYTES 25
+#define G64_FLAG_HIDDEN 0x01
+#define G64_FLAG_RANDOM_ACCESS 0x04
+
+typedef struct
+{
+ enum
+ {
+ UNIT_RTP,
+ UNIT_FRAMEMETA,
+ } type;
+ uint64_t pts;
+ uint8_t flags;
+ uint32_t size;
+} dump_unit_wrapper_t;
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+
+/*
+# Type ReceptionTime Intrval Size Keyfram Hidden First Last Count Error RTP TimeStamp
+
+0 AxisH264 (96) 24/12/2022 01:11:40.797 0 112978 Yes Yes 43704 43786 83 - 675574737
+BEX as 01D9172C4C2A22D0
+DEC as 13316314300 7970000
+
+1 AxisH264 (96) 24/12/2022 01:11:40.843 46 9641 Yes 43787 43793 7 - 675578337
+DEC as 13316314300 8430000
+
+5 AxisH264 (96) 24/12/2022 01:11:41.003 36 15424 Yes 43826 43836 11 - 675592735
+DEC as 13316314301 0030000
+13316314301 in secs is 154124 days, 0 hours, 11 minutes and 41 seconds.
+
+30 AxisH264 (96) 24/12/2022 01:11:42.003 30 14203 Yes 44102 44112 11 - 675682736
+DEC as 13316314302 0030000
+
+55 AxisH264 (96) 24/12/2022 01:11:43.003 36 13777 Yes 44365 44374 10 - 675772736
+DEC as 13316314303 0030000
+
+1555 AxisH264 (96) 24/12/2022 01:12:43.007 44 14217 61619 61629 11 - 681172723
+DEC as 13316314363 0070000
+
+6776 AxisH264 (96) 24/12/2022 01:16:11.840 40 14670 55442 55452 11 - 699968282
+DEC as 13316314571 8400000
+13316314571 in secs is 154124 days, 0 hours, 16 minutes and 11 seconds.
+
+Epoch + sub 10⁷ with Offset 0x2B61082F0 ?
+
+*/
+
+static vlc_tick_t make_vlc_timestamp( uint64_t ts )
+{
+ if( ts == 0xFFFFFFFFFFFFFFFFU ) /* This is All 0xFF */
+ return VLC_TICK_INVALID;
+ ts = (ts % 10000000) + ((ts / 10000000) - 0x2B61082F0) * 10000000;
+ return VLC_TICK_0 + ts / 10;
+}
+
+/*
+ {File Header}
+ |@000 30 Signature & version
+ |@030 08 End Time Timestamp
+ | FIXME Metadata starts from here.
+ | Might be variable number of entries. Go figure.
+ |@038 54 Fixedsize? Fixeddata? Unknown
+ |+000 07 Unknown
+ |+000 53 Fixed? Fixeddata? Unknown
+ |@090 20 Some Metadata header
+ |+000 16 Entity UUID
+ |+000 04 $M1 Metadata characters count in UTF16
+ |@110 $M1*2 Metadata bytes
+ | ( if version >= 5 )
+ |+000 20 Some Metadata header
+ |+000 16 Entity UUID
+ |+000 04 $M2 Metadata characters count in UTF16
+ |+000 $M2*2 Metadata bytes
+ |+000 60 Some UUID meta
+ |+000 16 Usage UUID
+ |+000 16 Origin UUID
+ |+000 16 MediaType UUID
+ |+000 04 Fixedsize? Fixeddata? Unkown
+ | ( if version >= 5 )
+ |+000 02 Fixedsize? Fixeddata? Unkown
+ |+000 04 Fixedsize? Fixeddata? Unkown
+ |+000 04 First Unit Header backward offset 0xFFFFFFFF
+
+ {RTP Dump Wrapper}
+ |+000 08 Timestamp
+ |+000 01 Flags
+ |+000 04 Wrapped packets size $VAR
+ |+000 12 ? 0x80 .. 0x30 (seems saved rtp packet header)
+ |+000 $VAR Wrapped packets
+ | [1..N]
+ |+000 06 RTP Packet Wrapper
+ |+000 02 Fixed? 0x00 (0x1F|0x1D)
+ |+000 02 Payload size $VAR2
+ |+000 02 Unknown (maybe high bytes for $VAR2)
+ |+000 $VAR2 RTP Packet
+ |+000 04 Total Wrapper size, backward navigation offset
+
+ {Frame Metadata}
+ |+000 08 Timestamp == 0xFFFFFFFFFFFFFFFF
+ |+000 04 Frame Meta Payload size $VAR
+ |+000 13 Unknown
+ |+000 $VAR Payload
+ |+000 06 Unknown
+ | [1..N]
+ |+000 51 Index Entry
+ |+000 18 Unknown
+ |+000 08 Timestamp
+ |+000 25 Unknown
+ |+000 04 Total size, backward navigation offset
+
+ {IFrame Index}
+ |+000 08 Timestamp == 0xFFFFFFFF
+ | [1..N]
+ |+000 08 Timestamp
+ |+000 08 File offset
+ |+000 02 Unknown
+ |+000 02 Unknown
+ |+000 04 Total size, backward navigation offset
+
+ File Layout
+ | {File Header}
+ | {RTP Dump Wrapper}
+ | {Frame Metadata}
+ | {IFrame Index}
+*/
+
+#if 0
+static int parse_framemetadata_payload( stream_t *s, uint32_t payloadsize )
+{
+ fprintf(stderr," parse_framemetadata_unitpayload @%"PRIu64"\n", vlc_stream_Tell(s));
+ if( vlc_stream_Read( s, NULL, 6 ) != 6 )
+ return VLC_EGENERIC;
+ payloadsize -= 6;
+ while( payloadsize >= 51 )
+ {
+ uint8_t unitheader[51];
+ if( vlc_stream_Read( s, unitheader, 51 ) != 51 )
+ return VLC_EGENERIC;
+ uint64_t ts = make_vlc_timestamp( GetQWLE( &unitheader[18] ) );
+ fprintf(stderr,"dump index %"PRId64" %u %u\n", ts,
+ GetDWLE( &unitheader[22] ), GetDWLE( &unitheader[26] ));
+ payloadsize -= 51;
+ }
+ assert(payloadsize == 0);
+ return payloadsize == 0 ? VLC_SUCCESS : VLC_EGENERIC;
+}
+#endif
+
+static int parse_iframeindex_payload( stream_t *s, uint32_t payloadsize, vec_index_t *index )
+{
+ msg_Dbg( s, "parsing index @%" PRIu64, vlc_stream_Tell(s) );
+
+ if( index && !vlc_vector_reserve( index, payloadsize / 18 ) )
+ return VLC_EGENERIC;
+
+ while( payloadsize > 18 )
+ {
+ uint8_t unitheader[18];
+ if( vlc_stream_Read( s, unitheader, 18 ) != 18 )
+ return VLC_EGENERIC;
+
+ index_t entry;
+ entry.time = make_vlc_timestamp( GetQWLE( &unitheader[0] ) );
+ entry.offset = GetQWLE( &unitheader[8] );
+ if( index )
+ vlc_vector_push( index, entry );
+
+ payloadsize -= 18;
+ }
+
+ if( payloadsize == 2 )
+ if( vlc_stream_Read( s, NULL, 2 ) != 2 )
+ return VLC_EGENERIC;
+
+ return payloadsize == 2 ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static int parse_metadata( stream_t *s, char **ppsz )
+{
+ uint8_t header[20];
+ if( vlc_stream_Read( s, header, 20 ) != 20 )
+ return VLC_EGENERIC;
+ uint32_t sz = GetDWLE( &header[16] ) * 2;
+ void *buf;
+ if( ppsz && (buf = malloc( sz )) )
+ {
+ int i_ret;
+ if( vlc_stream_Read( s, buf, sz ) == sz )
+ {
+ *ppsz = FromCharset( "UTF-16LE", buf, sz );
+ i_ret = *ppsz ? VLC_SUCCESS : VLC_EGENERIC;
+ }
+ else
+ i_ret = VLC_EGENERIC;
+ free( buf );
+ return i_ret;
+ }
+ else
+ {
+ if( vlc_stream_Read( s, NULL, sz ) != sz )
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+static int parse_file_header( stream_t *s, char **ppsz_title )
+{
+ const uint8_t *p_peek;
+ uint8_t prevoffset[4];
+ char *psz_title = NULL, **pp_meta = &psz_title;
+
+ if( vlc_stream_Peek( s, &p_peek, 7 ) != 7 ||
+ memcmp( p_peek, "Genetec", 7 ) )
+ return VLC_EGENERIC;
+
+ /* Version */
+ const uint8_t version = p_peek[26] - '0';
+ int uuidtextcount = version < 5 ? 1 : 2;
+ int uuidcount = version < 5 ? 0 : 3;
+
+ if( vlc_stream_Seek( s, 30 + 60 ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+
+ /* Parse UUID+text entries */
+ for( ; uuidtextcount; uuidtextcount-- )
+ {
+ if( parse_metadata( s, pp_meta ) != VLC_SUCCESS )
+ goto error;
+ pp_meta = NULL;
+ }
+
+ /* Parse UUID entries */
+ for( ; uuidcount; uuidcount-- )
+ {
+ if( vlc_stream_Read( s, NULL, 16 ) != 16 )
+ goto error;
+ }
+
+ ssize_t toread = version < 5 ? 10 : 12;
+ if (vlc_stream_Read( s, NULL, toread ) != toread ||
+ vlc_stream_Read( s, prevoffset, 4 ) != 4 ||
+ GetDWLE( prevoffset ) != 0xFFFFFFFFU )
+ goto error;
+
+ *ppsz_title = psz_title;
+
+ return VLC_SUCCESS;
+
+error:
+ free( psz_title );
+ return VLC_EGENERIC;
+}
+
+static int parse_wrapper_header( const uint8_t p[G64_WRAP_HEADER_BYTES], dump_unit_wrapper_t *unit )
+{
+#ifdef G64_DEBUG
+ fprintf(stderr, "dump unit: ");
+ for(int i=0; i<G64_WRAP_HEADER_BYTES; i++)
+ fprintf(stderr, "%2.2x ", p[i]);
+#endif
+ if( p[G64_WRAP_HEADER_BYTES - 1] == 0x30 && p[13] == 0x80 )
+ {
+ unit->type = UNIT_RTP;
+ unit->pts = make_vlc_timestamp( GetQWLE(&p[0]) );
+ unit->flags = p[8];
+ unit->size = GetDWLE(&p[9]);
+#ifdef G64_DEBUG
+ fprintf(stderr, "%"PRId64" %u %2.2x\n", unit->pts, unit->size, unit->flags);
+#endif
+ return VLC_SUCCESS;
+ }
+
+ return VLC_EGENERIC;
+}
+
+static int g64_rtp_pt_instantiate( vlc_object_t *obj, struct vlc_rtp_pt *restrict pt,
+ const struct vlc_sdp_pt *restrict desc, const char *mime )
+{
+ int ret = VLC_ENOTSUP;
+
+ module_t **mods;
+ ssize_t n = vlc_module_match( "rtp parser", mime, true, &mods, NULL );
+
+ for ( ssize_t i = 0; i < n; i++ )
+ {
+ vlc_rtp_parser_cb cb = vlc_module_map( vlc_object_logger(obj), mods[i] );
+ if ( cb == NULL )
+ continue;
+
+ ret = cb(obj, pt, desc);
+ if ( ret == VLC_SUCCESS )
+ {
+ msg_Dbg( obj, "- module \"%s\"", module_get_name(mods[i], true) );
+ assert( pt->ops != NULL );
+ ret = VLC_SUCCESS;
+ break;
+ }
+ }
+
+ free( mods );
+ return ret;
+}
+
+static void g64_dummy_es_destroy( struct vlc_rtp_es *es )
+{
+ VLC_UNUSED(es);
+}
+
+static void g64_dummy_es_decode( struct vlc_rtp_es *es, block_t *block )
+{
+ VLC_UNUSED(es);
+ block_Release( block );
+}
+
+static const struct vlc_rtp_es_operations g64_dummy_es_ops =
+{
+ g64_dummy_es_destroy, g64_dummy_es_decode,
+};
+
+struct g64_es_id {
+ struct vlc_rtp_es es;
+ es_out_t *out;
+ es_out_id_t *id;
+ vlc_tick_t *ppts;
+};
+
+static void g64_es_id_destroy( struct vlc_rtp_es *es )
+{
+ struct g64_es_id *ei = container_of(es, struct g64_es_id, es);
+ free( ei );
+}
+
+static void g64_es_id_send( struct vlc_rtp_es *es, block_t *block )
+{
+ struct g64_es_id *ei = container_of(es, struct g64_es_id, es);
+ block->i_pts = *ei->ppts;
+
+ es_out_Send( ei->out, ei->id, block );
+}
+
+static const struct vlc_rtp_es_operations g64_es_id_ops = {
+ g64_es_id_destroy, g64_es_id_send,
+};
+
+static struct vlc_rtp_es *g64_rtp_es_request( struct vlc_rtp_pt *pt,
+ const es_format_t *restrict fmt )
+{
+ demux_t *demux = pt->owner.data;
+ demux_sys_t *p_sys = demux->p_sys;
+ VLC_UNUSED(fmt);
+ assert(fmt->i_codec == p_sys->fcc);
+
+ struct g64_es_id *ei = malloc(sizeof (*ei));
+ if (unlikely(ei == NULL))
+ return &p_sys->dummy_es;
+
+ ei->es.ops = &g64_es_id_ops;
+ ei->out = demux->out;
+ ei->id = p_sys->es;
+ ei->ppts = &p_sys->pcr;
+ return &ei->es;
+}
+
+static struct vlc_rtp_es *g64_rtp_mux_request( struct vlc_rtp_pt *pt,
+ const char *name )
+{
+ VLC_UNUSED(pt);
+ VLC_UNUSED(name);
+ return NULL;
+}
+
+static void g64_rtp_pt_clear( struct vlc_rtp_pt *pt, void **pptriv )
+{
+ vlc_rtp_pt_end( pt, *pptriv );
+ pt->ops->release( pt );
+ *pptriv = NULL;
+}
+
+static const struct vlc_rtp_pt_owner_operations g64_pt_owner_ops = {
+ g64_rtp_es_request,
+ g64_rtp_mux_request,
+};
+
+static int g64_rtp_pt_init( demux_t *p_demux, struct vlc_rtp_pt *pt, vlc_fourcc_t fcc )
+{
+ struct vlc_rtp_pt_owner owner = {
+ &g64_pt_owner_ops, p_demux
+ };
+
+ static const struct
+ {
+ vlc_fourcc_t fcc;
+ const char *mime;
+ const char *parameters;
+ } payloads[] = {
+ { VLC_CODEC_H264, "video/H264", "packetization-mode=1;" },
+ { VLC_CODEC_HEVC, "video/H265", NULL },
+ { VLC_CODEC_MULAW, "audio/PCMU", NULL },
+ };
+
+ for(unsigned i=0;i<ARRAY_SIZE(payloads); i++)
+ {
+ if(payloads[i].fcc != fcc)
+ continue;
+
+ /* we mostly don't care about SDP values here,
+ we just need correct mime and parameters to open the pt */
+ struct vlc_sdp_pt sdp = {0};
+ sdp.channel_count = 2;
+ sdp.clock_rate = 90000;
+ strcat(sdp.name, strchr(payloads[i].mime,'/') + 1);
+ sdp.parameters = payloads[i].parameters;
+ pt->owner = owner;
+ pt->frequency = 90000;
+ pt->channel_count = 2;
+ pt->number = 96;
+
+ if ( g64_rtp_pt_instantiate( VLC_OBJECT(p_demux), pt, &sdp,
+ payloads[i].mime ) == VLC_SUCCESS )
+ return VLC_SUCCESS;
+
+ break;
+ }
+
+ return VLC_EGENERIC;
+}
+
+static int LoadIndex( stream_t *s, vec_index_t *index )
+{
+ uint64_t i_size;
+ const uint8_t *p_peek;
+
+ if( vlc_stream_GetSize( s, &i_size ) != VLC_SUCCESS ||
+ vlc_stream_Seek( s, i_size - 4 ) != VLC_SUCCESS ||
+ vlc_stream_Peek( s, &p_peek, 4 ) != 4 )
+ return VLC_EGENERIC;
+
+ uint32_t i_offset = GetDWLE( p_peek );
+ if( i_offset < 14 + 18 ||
+ i_offset > i_size - 4 - 4 ||
+ vlc_stream_Seek( s, i_size - i_offset - 4 - 4 ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+
+ uint8_t header[12];
+ if( vlc_stream_Read( s, header, 12 ) != 12 ||
+ GetQWLE( &header[4] ) != 0xFFFFFFFFFFFFFFFFU )
+ return VLC_EGENERIC;
+
+ return parse_iframeindex_payload( s, i_offset + 4 - 12, index );
+}
+
+static int BuildIndex( stream_t *s, vec_index_t *index )
+{
+ for( ;; )
+ {
+ index_t entry = { 0, vlc_stream_Tell(s) };
+
+ uint8_t unit_header[G64_WRAP_HEADER_BYTES];
+ if( vlc_stream_Read( s, unit_header, G64_WRAP_HEADER_BYTES )
+ != G64_WRAP_HEADER_BYTES )
+ break;
+
+ dump_unit_wrapper_t unit;
+ if( parse_wrapper_header(unit_header, &unit) != VLC_SUCCESS ||
+ unit.size < 12 || unit.type != UNIT_RTP )
+ break;
+
+ if( unit.flags & G64_FLAG_RANDOM_ACCESS )
+ {
+ entry.time = unit.pts;
+ if( !vlc_vector_push( index, entry ) )
+ break;
+ }
+
+ if( vlc_stream_Seek( s, vlc_stream_Tell(s) + unit.size - 8 ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+ }
+
+ return index->size ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static void parse_rtp( demux_t *p_demux, block_t *p_block )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if ( p_block->i_buffer < 12 )
+ goto end;
+
+ const uint8_t version = p_block->p_buffer[0] >> 6;
+ const uint8_t pad = p_block->p_buffer[0] & 0x20;
+ const uint8_t ext = p_block->p_buffer[0] & 0x10;
+ const uint8_t cc = p_block->p_buffer[0] & 0x0f;
+ const uint8_t mbit = p_block->p_buffer[1] & 0x80;
+ //const uint8_t pt = p_block->p_buffer[1] & 0x7f;
+
+ if ( version != 0x02 )
+ goto end;
+
+ p_block->p_buffer += 12;
+ p_block->i_buffer -= 12;
+
+ if ( pad )
+ {
+ if( p_block->i_buffer < 2 )
+ goto end;
+
+ uint8_t trunc = p_block->p_buffer[p_block->i_buffer - 1];
+ if (trunc > 0 && trunc < p_block->i_buffer)
+ p_block->i_buffer -= trunc;
+ else
+ goto end;
+ }
+
+ if ( cc )
+ {
+ if( cc * 4 > p_block->i_buffer )
+ goto end;
+ p_block->p_buffer += cc * 4;
+ p_block->i_buffer -= cc * 4;
+ }
+
+ if ( ext )
+ {
+ if ( p_block->i_buffer < 4 )
+ goto end;
+ unsigned extlen = GetWBE( &p_block->p_buffer[2] );
+ if( extlen * 4 > p_block->i_buffer - 4 )
+ goto end;
+ p_block->p_buffer += 4 + extlen * 4;
+ p_block->i_buffer -= 4 + extlen * 4;
+ }
+
+ const struct vlc_rtp_pktinfo pktinfo = { .m = mbit };
+ vlc_rtp_pt_decode( &p_sys->pt, p_sys->ptpriv, p_block, &pktinfo );
+
+ return;
+
+end:
+ block_Release( p_block );
+}
+
+static int parse_rtp_dumps( demux_t *p_demux, uint32_t size )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ while( size >= 6 )
+ {
+ /* Process one packet dump */
+ uint8_t header[6];
+ if( vlc_stream_Read( p_demux->s, header, 6 ) != 6 )
+ break;
+
+ if( header[0] != 0x00 || header[1] != p_sys->i_type )
+ break;
+
+ uint16_t sz = GetWBE( &header[2] );
+
+ if( sz > size + 6 )
+ break;
+
+ block_t *p_block = vlc_stream_Block( p_demux->s, sz );
+ if( p_block == NULL )
+ break;
+
+ parse_rtp( p_demux, p_block );
+
+ size -= 6 + sz;
+ }
+
+ return (size != 0) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ uint8_t unit_header[G64_WRAP_HEADER_BYTES];
+ if( vlc_stream_Read( p_demux->s, unit_header, G64_WRAP_HEADER_BYTES )
+ != G64_WRAP_HEADER_BYTES )
+ return VLC_DEMUXER_EOF;
+
+ dump_unit_wrapper_t unit;
+ if( parse_wrapper_header( unit_header, &unit ) != VLC_SUCCESS ||
+ unit.size < 12 || unit.type != UNIT_RTP )
+ return VLC_DEMUXER_EOF;
+
+ if ( p_sys->pcr == VLC_TICK_INVALID && unit.pts != VLC_TICK_INVALID )
+ es_out_SetPCR( p_demux->out, unit.pts );
+ p_sys->pcr = unit.pts;
+
+ int ret = parse_rtp_dumps( p_demux, unit.size - 12 );
+ uint8_t wrappersize[4];
+ if(ret != VLC_DEMUXER_SUCCESS)
+ {
+ msg_Err( p_demux, "Invalid wrapper bytes @%"PRIu64,
+ vlc_stream_Tell(p_demux->s) - 6);
+ ret = VLC_DEMUXER_EOF;
+ }
+ else if( vlc_stream_Read( p_demux->s, wrappersize, 4 ) != 4 ||
+ GetDWLE(wrappersize) != unit.size + 13 )
+ {
+ msg_Err( p_demux, "Invalid end of wrapper @%"PRIu64,
+ vlc_stream_Tell(p_demux->s) );
+ ret = VLC_DEMUXER_EOF;
+ }
+
+ if( p_sys->pcr != VLC_TICK_INVALID )
+ es_out_SetPCR( p_demux->out, p_sys->pcr );
+
+ return ret;
+}
+
+static int SeekTo( demux_t *p_demux, vlc_tick_t ts, bool b_precise )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if( p_sys->index.size == 0 )
+ return VLC_EGENERIC;
+
+ /* delete and replace session first as we can't reset */
+ struct vlc_rtp_pt newpt = {0};
+ if( g64_rtp_pt_init( p_demux, &newpt, p_sys->fcc ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+ g64_rtp_pt_clear( &p_sys->pt, &p_sys->ptpriv );
+ p_sys->pt = newpt;
+ p_sys->ptpriv = vlc_rtp_pt_begin( &p_sys->pt );
+
+ uint64_t offset = p_sys->i_start_offset;
+ for( size_t i=0; i<p_sys->index.size; i++ )
+ {
+ if( p_sys->index.data[i].time > ts )
+ break;
+ offset = p_sys->index.data[i].offset;
+ }
+
+ p_sys->pcr = VLC_TICK_INVALID;
+ int ret = vlc_stream_Seek( p_demux->s, offset );
+ if( ret == VLC_SUCCESS && b_precise )
+ b_precise = es_out_SetNextDisplayTime( p_demux->out, ts );
+ return ret;
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ switch( i_query )
+ {
+ case DEMUX_GET_LENGTH:
+ {
+ *va_arg(args, vlc_tick_t *) = p_sys->i_duration;
+ return VLC_SUCCESS;
+ }
+
+ case DEMUX_GET_NORMAL_TIME:
+ {
+ if( p_sys->i_first_pts != VLC_TICK_INVALID )
+ {
+ *va_arg(args, vlc_tick_t *) = p_sys->i_first_pts;
+ return VLC_SUCCESS;
+ }
+ return VLC_EGENERIC;
+ }
+
+ case DEMUX_GET_TIME:
+ if( p_sys->pcr == VLC_TICK_INVALID )
+ return VLC_EGENERIC;
+ *va_arg(args, vlc_tick_t *) = p_sys->pcr;
+ return VLC_SUCCESS;
+
+ case DEMUX_SET_TIME:
+ {
+ vlc_tick_t ts = va_arg(args, vlc_tick_t);
+ bool b_precise = va_arg( args, int );
+ return SeekTo( p_demux, p_sys->i_first_pts + ts, b_precise );
+ }
+
+ case DEMUX_GET_POSITION:
+ {
+ if( p_sys->i_duration == 0 || p_sys->pcr == VLC_TICK_INVALID )
+ return VLC_EGENERIC;
+ *va_arg(args, double *) = (p_sys->pcr - p_sys->i_first_pts) / (double) p_sys->i_duration;
+ return VLC_SUCCESS;
+ }
+
+ case DEMUX_SET_POSITION:
+ {
+ if( p_sys->i_duration == 0 )
+ return VLC_EGENERIC;
+ double f = va_arg(args, double);
+ bool b_precise = va_arg( args, int );
+ return SeekTo( p_demux, p_sys->i_first_pts + p_sys->i_duration * f, b_precise );
+ }
+
+ case DEMUX_GET_META:
+ {
+ vlc_meta_t *p_meta;
+ if ( p_sys->psz_title && (p_meta = va_arg(args, vlc_meta_t *)) )
+ {
+ vlc_meta_Set( p_meta, vlc_meta_Title, p_sys->psz_title );
+ return VLC_SUCCESS;
+ }
+ return VLC_EGENERIC;
+ }
+
+ default:
+ return demux_vaControlHelper( p_demux->s, 0, -1, -1, -1,
+ i_query, args );
+ }
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t*) p_this;
+ demux_sys_t *p_sys = p_demux->p_sys;
+ if( p_sys->pt.ops )
+ g64_rtp_pt_clear( &p_sys->pt, &p_sys->ptpriv );
+ if( p_sys->es )
+ es_out_Del( p_demux->out, p_sys->es );
+ vlc_vector_clear( &p_sys->index );
+ free( p_sys->psz_title );
+ free( p_sys );
+}
+
+/*****************************************************************************
+ * Open:
+ *****************************************************************************/
+
+static int Open( vlc_object_t * p_this )
+{
+ demux_t *p_demux = (demux_t*) p_this;
+ demux_sys_t *p_sys;
+ es_format_t fmt;
+ char *psz_title = NULL;
+
+ if( parse_file_header( p_demux->s, &psz_title ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+
+ const uint8_t *p_peek;
+ if( vlc_stream_Peek( p_demux->s, &p_peek, G64_WRAP_HEADER_BYTES + 6 ) !=
+ G64_WRAP_HEADER_BYTES + 6 )
+ return VLC_EGENERIC;
+
+ switch( p_peek[G64_WRAP_HEADER_BYTES + 1] )
+ {
+ case 0x1F:
+ case 0x1D:
+ es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_H264 );
+ break;
+ case 0x3D:
+ es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_HEVC );
+ break;
+ case 0xD2:
+ es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_MULAW );
+ fmt.audio.i_channels = 1;
+ fmt.audio.i_rate = 8000;
+ break;
+ default:
+ msg_Err(p_this, "unknown %2.2x", p_peek[G64_WRAP_HEADER_BYTES + 1]);
+ free( psz_title );
+ return VLC_EGENERIC;
+ }
+
+ p_demux->p_sys = p_sys = calloc( 1, sizeof(demux_sys_t) );
+ if( !p_sys )
+ {
+ free( psz_title );
+ return VLC_ENOMEM;
+ }
+
+ p_sys->i_type = p_peek[G64_WRAP_HEADER_BYTES + 1];
+ vlc_vector_init( &p_sys->index );
+ p_sys->i_start_offset = vlc_stream_Tell( p_demux->s );
+ p_sys->psz_title = psz_title;
+
+ p_sys->fcc = fmt.i_codec;
+ fmt.b_packetized = true;
+
+ p_sys->es = es_out_Add( p_demux->out, &fmt );
+ if( !p_sys->es )
+ {
+ Close( p_this );
+ return VLC_EGENERIC;
+ }
+ p_sys->dummy_es.ops = &g64_dummy_es_ops;
+
+ /* Build RTP pt */
+ if( g64_rtp_pt_init( p_demux, &p_sys->pt, fmt.i_codec ) != VLC_SUCCESS )
+ {
+ Close( p_this );
+ return VLC_EGENERIC;
+ }
+
+ p_sys->ptpriv = vlc_rtp_pt_begin( &p_sys->pt );
+
+ if( !p_demux->b_preparsing && vlc_stream_CanSeek( p_demux->s ) )
+ {
+ /* Load native index from end of file */
+ int ret = LoadIndex( p_demux->s, &p_sys->index );
+ if( vlc_stream_Seek( p_demux->s, p_sys->i_start_offset ) )
+ {
+ Close( p_this );
+ return VLC_EGENERIC;
+ }
+
+ /* Build Index if file is truncated */
+ if( ret != VLC_SUCCESS && vlc_stream_CanFastSeek( p_demux->s ) )
+ {
+ msg_Warn( p_demux, "Can't load index. Trying to rebuild" );
+ if( BuildIndex( p_demux->s, &p_sys->index ) != VLC_SUCCESS )
+ msg_Warn( p_demux, "Can't build index. Won't be able to seek" );
+ if( vlc_stream_Seek( p_demux->s, p_sys->i_start_offset ) )
+ {
+ Close( p_this );
+ return VLC_EGENERIC;
+ }
+ }
+ }
+
+ if( p_sys->index.size > 0 )
+ {
+ p_sys->i_first_pts = p_sys->index.data[0].time;
+ p_sys->i_duration = p_sys->index.data[p_sys->index.size -1].time - p_sys->i_first_pts;
+ }
+ else
+ {
+ p_sys->i_first_pts = VLC_TICK_INVALID;
+ p_sys->i_duration = 0;
+ }
+
+ p_demux->pf_demux = Demux;
+ p_demux->pf_control = Control;
+ return VLC_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+vlc_module_begin ()
+ set_shortname( "G64 raw RTP" )
+ set_description( N_("G64 Raw RTP") )
+ set_capability( "demux", 10 )
+ set_subcategory( SUBCAT_INPUT_DEMUX )
+ set_callbacks( Open, Close )
+vlc_module_end ()
=====================================
modules/demux/meson.build
=====================================
@@ -84,6 +84,12 @@ vlc_modules += {
'sources' : files('hx.c'),
}
+# G64 RTP dumps demuxer
+vlc_modules += {
+ 'name' : 'g64rtp',
+ 'sources' : files('g64rtp.c'),
+}
+
# MPEG PS demux
vlc_modules += {
'name' : 'ps',
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/08630bfbdf05734110aef66b5b1c7657b09d7ab3
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/08630bfbdf05734110aef66b5b1c7657b09d7ab3
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list