|
|
|
|
Quelle g64rtp.c
Sprache: C
|
|
/*****************************************************************************
* 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, 27 ) != 27 ||
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 );
if (pt->ops->release != NULL)
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 )
{
free( psz_title );
return VLC_EGENERIC;
}
switch( p_peek[G64_WRAP_HEADER_BYTES + 1] )
{
case 0x1F:
case 0x1D:
case 0x21:
case 0x31:
case 0x34:
es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_H264 );
break;
case 0x3D:
case 0x41:
case 0x43:
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 ()
¤ Dauer der Verarbeitung: 0.7 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104856 |
| #Domains=417287 |
|
|