Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dvblast
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
dvblast
Commits
58845e9b
Commit
58845e9b
authored
Aug 13, 2009
by
Andy Gatward
Committed by
Christophe Massiot
Aug 13, 2009
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
* ALL: EPG pass-through support, with SDT regeneration.
parent
39709170
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
231 additions
and
8 deletions
+231
-8
demux.c
demux.c
+164
-6
dvblast.c
dvblast.c
+8
-2
dvblast.h
dvblast.h
+5
-0
output.c
output.c
+4
-0
util.c
util.c
+50
-0
No files found.
demux.c
View file @
58845e9b
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
* $Id$
* $Id$
*
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Andy Gatward <a.j.gatward@reading.ac.uk>
*
*
* This program is free software; you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* it under the terms of the GNU General Public License as published by
...
@@ -34,12 +35,17 @@
...
@@ -34,12 +35,17 @@
#include <dvbpsi/demux.h>
#include <dvbpsi/demux.h>
#include <dvbpsi/pat.h>
#include <dvbpsi/pat.h>
#include <dvbpsi/eit.h>
#include <dvbpsi/eit.h>
#include <dvbpsi/sdt.h>
#include <dvbpsi/dr.h>
#include <dvbpsi/dr.h>
#include <dvbpsi/psi.h>
#include <dvbpsi/psi.h>
/*****************************************************************************
/*****************************************************************************
* Local declarations
* Local declarations
*****************************************************************************/
*****************************************************************************/
#define SDT_PID 0x11
#define EIT_PID 0x12
typedef
struct
ts_pid_t
typedef
struct
ts_pid_t
{
{
int
i_refcount
;
int
i_refcount
;
...
@@ -63,6 +69,8 @@ static sid_t **pp_sids = NULL;
...
@@ -63,6 +69,8 @@ static sid_t **pp_sids = NULL;
static
int
i_nb_sids
=
0
;
static
int
i_nb_sids
=
0
;
static
dvbpsi_handle
p_pat_dvbpsi_handle
;
static
dvbpsi_handle
p_pat_dvbpsi_handle
;
static
dvbpsi_handle
p_sdt_dvbpsi_handle
;
static
dvbpsi_handle
p_eit_dvbpsi_handle
;
static
dvbpsi_pat_t
*
p_current_pat
=
NULL
;
static
dvbpsi_pat_t
*
p_current_pat
=
NULL
;
static
int
i_demux_fd
;
static
int
i_demux_fd
;
...
@@ -90,6 +98,9 @@ static void NewPAT( output_t *p_output );
...
@@ -90,6 +98,9 @@ static void NewPAT( output_t *p_output );
static
void
NewPMT
(
output_t
*
p_output
);
static
void
NewPMT
(
output_t
*
p_output
);
static
void
PATCallback
(
void
*
_unused
,
dvbpsi_pat_t
*
p_pat
);
static
void
PATCallback
(
void
*
_unused
,
dvbpsi_pat_t
*
p_pat
);
static
void
PMTCallback
(
void
*
_unused
,
dvbpsi_pmt_t
*
p_pmt
);
static
void
PMTCallback
(
void
*
_unused
,
dvbpsi_pmt_t
*
p_pmt
);
static
void
PSITableCallback
(
void
*
_unused
,
dvbpsi_handle
h_dvbpsi
,
uint8_t
i_table_id
,
uint16_t
i_extension
);
static
void
SDTCallback
(
void
*
_unused
,
dvbpsi_sdt_t
*
p_sdt
);
/*****************************************************************************
/*****************************************************************************
* demux_Open
* demux_Open
...
@@ -113,6 +124,15 @@ void demux_Open( void )
...
@@ -113,6 +124,15 @@ void demux_Open( void )
SetPID
(
0
);
/* PAT */
SetPID
(
0
);
/* PAT */
p_pat_dvbpsi_handle
=
dvbpsi_AttachPAT
(
PATCallback
,
NULL
);
p_pat_dvbpsi_handle
=
dvbpsi_AttachPAT
(
PATCallback
,
NULL
);
if
(
b_enable_epg
)
{
SetPID
(
SDT_PID
);
/* SDT */
p_sdt_dvbpsi_handle
=
dvbpsi_AttachDemux
(
PSITableCallback
,
NULL
);
SetPID
(
EIT_PID
);
/* EIT */
p_eit_dvbpsi_handle
=
dvbpsi_AttachDemux
(
PSITableCallback
,
NULL
);
}
}
}
/*****************************************************************************
/*****************************************************************************
...
@@ -165,6 +185,14 @@ static void demux_Handle( block_t *p_ts )
...
@@ -165,6 +185,14 @@ static void demux_Handle( block_t *p_ts )
if
(
block_UnitStart
(
p_ts
)
)
if
(
block_UnitStart
(
p_ts
)
)
SendPAT
();
SendPAT
();
}
}
else
if
(
i_pid
==
EIT_PID
)
{
dvbpsi_PushPacket
(
p_eit_dvbpsi_handle
,
p_ts
->
p_ts
);
}
else
if
(
i_pid
==
SDT_PID
)
{
dvbpsi_PushPacket
(
p_sdt_dvbpsi_handle
,
p_ts
->
p_ts
);
}
else
else
{
{
for
(
i
=
0
;
i
<
i_nb_sids
;
i
++
)
for
(
i
=
0
;
i
<
i_nb_sids
;
i
++
)
...
@@ -655,14 +683,66 @@ static void SendPMT( sid_t *p_sid )
...
@@ -655,14 +683,66 @@ static void SendPMT( sid_t *p_sid )
for
(
i
=
0
;
i
<
i_nb_outputs
;
i
++
)
for
(
i
=
0
;
i
<
i_nb_outputs
;
i
++
)
{
{
if
(
pp_outputs
[
i
]
->
i_maddr
&&
pp_outputs
[
i
]
->
i_sid
==
p_sid
->
i_sid
if
(
pp_outputs
[
i
]
->
i_maddr
&&
pp_outputs
[
i
]
->
i_sid
==
p_sid
->
i_sid
)
&&
pp_outputs
[
i
]
->
p_pmt_section
!=
NULL
)
{
{
if
(
pp_outputs
[
i
]
->
p_pmt_section
!=
NULL
)
{
block_t
*
p_block
;
p_block
=
WritePSISection
(
pp_outputs
[
i
]
->
p_pmt_section
,
p_sid
->
i_pmt_pid
,
&
pp_outputs
[
i
]
->
i_pmt_cc
);
while
(
p_block
!=
NULL
)
{
block_t
*
p_next
=
p_block
->
p_next
;
p_block
->
i_refcount
--
;
output_Put
(
pp_outputs
[
i
],
p_block
);
p_block
=
p_next
;
}
}
if
(
pp_outputs
[
i
]
->
p_sdt_section
!=
NULL
)
{
block_t
*
p_block
;
p_block
=
WritePSISection
(
pp_outputs
[
i
]
->
p_sdt_section
,
SDT_PID
,
&
pp_outputs
[
i
]
->
i_sdt_cc
);
while
(
p_block
!=
NULL
)
{
block_t
*
p_next
=
p_block
->
p_next
;
p_block
->
i_refcount
--
;
output_Put
(
pp_outputs
[
i
],
p_block
);
p_block
=
p_next
;
}
}
}
}
}
/*****************************************************************************
* SendEIT
*****************************************************************************/
static
void
SendEIT
(
dvbpsi_psi_section_t
*
p_section
,
uint16_t
i_sid
,
uint8_t
i_table_id
)
{
int
i
;
for
(
i
=
0
;
i
<
i_nb_outputs
;
i
++
)
{
if
(
pp_outputs
[
i
]
->
i_maddr
&&
pp_outputs
[
i
]
->
i_sid
==
i_sid
)
{
p_section
->
p_data
[
8
]
=
(
i_sid
>>
8
)
&
0xff
;
p_section
->
p_data
[
9
]
=
i_sid
&
0xff
;
p_section
->
p_data
[
13
]
=
pp_outputs
[
i
]
->
i_eit_last_table_id
;
dvbpsi_BuildPSISection
(
p_section
);
block_t
*
p_block
;
block_t
*
p_block
;
p_block
=
WritePSISection
(
p
p_outputs
[
i
]
->
p_pmt
_section
,
p_block
=
WritePSISection
(
p_section
,
p_sid
->
i_pmt_pid
,
EIT_PID
,
&
pp_outputs
[
i
]
->
i_
pm
t_cc
);
&
pp_outputs
[
i
]
->
i_
ei
t_cc
);
while
(
p_block
!=
NULL
)
while
(
p_block
!=
NULL
)
{
{
block_t
*
p_next
=
p_block
->
p_next
;
block_t
*
p_next
=
p_block
->
p_next
;
...
@@ -891,7 +971,7 @@ static int PMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
...
@@ -891,7 +971,7 @@ static int PMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
for
(
p_dr
=
p_pmt
->
p_first_descriptor
;
p_dr
!=
NULL
;
p_dr
=
p_dr
->
p_next
)
for
(
p_dr
=
p_pmt
->
p_first_descriptor
;
p_dr
!=
NULL
;
p_dr
=
p_dr
->
p_next
)
if
(
p_dr
->
i_tag
==
0x9
)
if
(
p_dr
->
i_tag
==
0x9
)
return
1
;
return
1
;
for
(
p_es
=
p_pmt
->
p_first_es
;
p_es
!=
NULL
;
p_es
=
p_es
->
p_next
)
for
(
p_es
=
p_pmt
->
p_first_es
;
p_es
!=
NULL
;
p_es
=
p_es
->
p_next
)
for
(
p_dr
=
p_es
->
p_first_descriptor
;
p_dr
!=
NULL
;
for
(
p_dr
=
p_es
->
p_first_descriptor
;
p_dr
!=
NULL
;
p_dr
=
p_dr
->
p_next
)
p_dr
=
p_dr
->
p_next
)
...
@@ -1160,3 +1240,81 @@ static void PMTCallback( void *_unused, dvbpsi_pmt_t *p_pmt )
...
@@ -1160,3 +1240,81 @@ static void PMTCallback( void *_unused, dvbpsi_pmt_t *p_pmt )
UpdatePMT
(
p_pmt
->
i_program_number
);
UpdatePMT
(
p_pmt
->
i_program_number
);
}
}
static
void
PSITableCallback
(
void
*
_unused
,
dvbpsi_handle
h_dvbpsi
,
uint8_t
i_table_id
,
uint16_t
i_extension
)
{
/* EIT tables */
if
(
i_table_id
==
0x43
||
(
i_table_id
>=
0x50
&&
i_table_id
<=
0x5f
)
)
{
SendEIT
(
h_dvbpsi
->
p_current_section
,
i_extension
,
i_table_id
);
}
/* SDT tables */
if
(
i_table_id
==
0x42
)
{
dvbpsi_AttachSDT
(
h_dvbpsi
,
i_table_id
,
i_extension
,
SDTCallback
,
NULL
);
}
}
static
void
SDTCallback
(
void
*
_unused
,
dvbpsi_sdt_t
*
p_sdt
)
{
int
i
;
dvbpsi_sdt_service_t
*
p_service
=
p_sdt
->
p_first_service
;
dvbpsi_descriptor_t
*
p_descriptor
;
dvbpsi_sdt_t
*
p_new_sdt
=
NULL
;
dvbpsi_sdt_service_t
*
p_new_service
;
dvbpsi_psi_section_t
*
p_old_section
;
msg_Dbg
(
NULL
,
"new SDT, version %d"
,
p_sdt
->
i_version
);
while
(
p_service
!=
NULL
)
{
for
(
i
=
0
;
i
<
i_nb_outputs
;
i
++
)
{
if
(
pp_outputs
[
i
]
->
i_maddr
&&
pp_outputs
[
i
]
->
i_sid
==
p_service
->
i_service_id
)
{
p_new_sdt
=
malloc
(
sizeof
(
dvbpsi_sdt_t
));
dvbpsi_InitSDT
(
p_new_sdt
,
p_service
->
i_service_id
,
p_sdt
->
i_version
,
p_sdt
->
b_current_next
,
p_sdt
->
i_network_id
);
p_new_service
=
dvbpsi_SDTAddService
(
p_new_sdt
,
p_service
->
i_service_id
,
p_service
->
b_eit_schedule
,
p_service
->
b_eit_present
,
p_service
->
i_running_status
,
0
);
p_descriptor
=
p_service
->
p_first_descriptor
;
while
(
p_descriptor
!=
NULL
)
{
dvbpsi_SDTServiceAddDescriptor
(
p_new_service
,
p_descriptor
->
i_tag
,
p_descriptor
->
i_length
,
p_descriptor
->
p_data
);
p_descriptor
=
p_descriptor
->
p_next
;
}
p_old_section
=
pp_outputs
[
i
]
->
p_sdt_section
;
pp_outputs
[
i
]
->
p_sdt_section
=
dvbpsi_GenSDTSections
(
p_new_sdt
);
if
(
p_old_section
!=
NULL
)
{
dvbpsi_DeletePSISections
(
p_old_section
);
}
dvbpsi_DeleteSDT
(
p_new_sdt
);
}
}
p_service
=
p_service
->
p_next
;
}
dvbpsi_DeleteSDT
(
p_sdt
);
}
dvblast.c
View file @
58845e9b
...
@@ -55,6 +55,7 @@ int i_bandwidth = 8;
...
@@ -55,6 +55,7 @@ int i_bandwidth = 8;
char
*
psz_modulation
=
NULL
;
char
*
psz_modulation
=
NULL
;
int
b_budget_mode
=
0
;
int
b_budget_mode
=
0
;
int
b_output_udp
=
0
;
int
b_output_udp
=
0
;
int
b_enable_epg
=
0
;
volatile
int
b_hup_received
=
0
;
volatile
int
b_hup_received
=
0
;
int
i_verbose
=
DEFAULT_VERBOSITY
;
int
i_verbose
=
DEFAULT_VERBOSITY
;
...
@@ -183,7 +184,7 @@ static void SigHandler( int i_signal )
...
@@ -183,7 +184,7 @@ static void SigHandler( int i_signal )
*****************************************************************************/
*****************************************************************************/
void
usage
()
void
usage
()
{
{
msg_Raw
(
NULL
,
"Usage: dvblast [-q] -c <config file> [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] [-i <RT priority>] [-a <adapter>][-n <frontend_num>] -f <frequency> [-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-m <modulation] [-u] [-U] [-d <dest IP:port>]"
);
msg_Raw
(
NULL
,
"Usage: dvblast [-q] -c <config file> [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] [-i <RT priority>] [-a <adapter>][-n <frontend_num>] -f <frequency> [-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-m <modulation] [-u] [-U] [-d <dest IP:port>]
[-e]
"
);
msg_Raw
(
NULL
,
" -q: be quiet (less verbosity, repeat or use number for even quieter)"
);
msg_Raw
(
NULL
,
" -q: be quiet (less verbosity, repeat or use number for even quieter)"
);
msg_Raw
(
NULL
,
" -v: voltage to apply to the LNB (QPSK)"
);
msg_Raw
(
NULL
,
" -v: voltage to apply to the LNB (QPSK)"
);
msg_Raw
(
NULL
,
" -p: force 22kHz pulses for high-band selection (DVB-S)"
);
msg_Raw
(
NULL
,
" -p: force 22kHz pulses for high-band selection (DVB-S)"
);
...
@@ -193,6 +194,7 @@ void usage()
...
@@ -193,6 +194,7 @@ void usage()
msg_Raw
(
NULL
,
" -u: turn on budget mode (no hardware PID filtering)"
);
msg_Raw
(
NULL
,
" -u: turn on budget mode (no hardware PID filtering)"
);
msg_Raw
(
NULL
,
" -U: use raw UDP rather than RTP (required by some IPTV set top boxes)"
);
msg_Raw
(
NULL
,
" -U: use raw UDP rather than RTP (required by some IPTV set top boxes)"
);
msg_Raw
(
NULL
,
" -d: duplicate all received packets to a given port"
);
msg_Raw
(
NULL
,
" -d: duplicate all received packets to a given port"
);
msg_Raw
(
NULL
,
" -e: enable EPG pass through (EIT data)"
);
exit
(
1
);
exit
(
1
);
}
}
...
@@ -207,7 +209,7 @@ int main( int i_argc, char **pp_argv )
...
@@ -207,7 +209,7 @@ int main( int i_argc, char **pp_argv )
msg_Warn
(
NULL
,
"restarting"
);
msg_Warn
(
NULL
,
"restarting"
);
while
(
(
c
=
getopt
(
i_argc
,
pp_argv
,
"q::c:r:t:o:i:a:n:f:s:v:pb:m:uUd:h"
))
!=
(
char
)
EOF
)
while
(
(
c
=
getopt
(
i_argc
,
pp_argv
,
"q::c:r:t:o:i:a:n:f:s:v:pb:m:uUd:
e
h"
))
!=
(
char
)
EOF
)
{
{
switch
(
c
)
switch
(
c
)
{
{
...
@@ -315,6 +317,10 @@ int main( int i_argc, char **pp_argv )
...
@@ -315,6 +317,10 @@ int main( int i_argc, char **pp_argv )
break
;
break
;
}
}
case
'e'
:
b_enable_epg
=
1
;
break
;
case
'h'
:
case
'h'
:
default:
default:
usage
();
usage
();
...
...
dvblast.h
View file @
58845e9b
...
@@ -65,6 +65,9 @@ typedef struct output_t
...
@@ -65,6 +65,9 @@ typedef struct output_t
uint8_t
i_pat_version
,
i_pat_cc
;
uint8_t
i_pat_version
,
i_pat_cc
;
dvbpsi_psi_section_t
*
p_pmt_section
;
dvbpsi_psi_section_t
*
p_pmt_section
;
uint8_t
i_pmt_version
,
i_pmt_cc
;
uint8_t
i_pmt_version
,
i_pmt_cc
;
dvbpsi_psi_section_t
*
p_sdt_section
;
uint8_t
i_sdt_cc
;
uint8_t
i_eit_cc
,
i_eit_last_table_id
;
/* configuration */
/* configuration */
uint16_t
i_sid
;
/* 0 if raw mode */
uint16_t
i_sid
;
/* 0 if raw mode */
...
@@ -91,6 +94,7 @@ extern int i_bandwidth;
...
@@ -91,6 +94,7 @@ extern int i_bandwidth;
extern
char
*
psz_modulation
;
extern
char
*
psz_modulation
;
extern
int
b_budget_mode
;
extern
int
b_budget_mode
;
extern
int
b_output_udp
;
extern
int
b_output_udp
;
extern
int
b_enable_epg
;
extern
volatile
int
b_hup_received
;
extern
volatile
int
b_hup_received
;
extern
mtime_t
i_ca_timeout
;
extern
mtime_t
i_ca_timeout
;
extern
int
i_comm_fd
;
extern
int
i_comm_fd
;
...
@@ -105,6 +109,7 @@ void msg_Dbg( void *_unused, const char *psz_format, ... );
...
@@ -105,6 +109,7 @@ void msg_Dbg( void *_unused, const char *psz_format, ... );
void
msg_Raw
(
void
*
_unused
,
const
char
*
psz_format
,
...
);
void
msg_Raw
(
void
*
_unused
,
const
char
*
psz_format
,
...
);
mtime_t
mdate
(
void
);
mtime_t
mdate
(
void
);
void
msleep
(
mtime_t
delay
);
void
msleep
(
mtime_t
delay
);
void
hexDump
(
uint8_t
*
p_data
,
uint32_t
i_len
);
void
dvb_Open
(
void
);
void
dvb_Open
(
void
);
block_t
*
dvb_Read
(
void
);
block_t
*
dvb_Read
(
void
);
...
...
output.c
View file @
58845e9b
...
@@ -87,12 +87,16 @@ int output_Init( output_t *p_output, in_addr_t i_maddr, uint16_t i_port )
...
@@ -87,12 +87,16 @@ int output_Init( output_t *p_output, in_addr_t i_maddr, uint16_t i_port )
p_output
->
i_cc
=
rand
()
&
0xffff
;
p_output
->
i_cc
=
rand
()
&
0xffff
;
p_output
->
i_pat_cc
=
rand
()
&
0xf
;
p_output
->
i_pat_cc
=
rand
()
&
0xf
;
p_output
->
i_pmt_cc
=
rand
()
&
0xf
;
p_output
->
i_pmt_cc
=
rand
()
&
0xf
;
p_output
->
i_sdt_cc
=
rand
()
&
0xf
;
p_output
->
i_eit_cc
=
rand
()
&
0xf
;
p_output
->
i_pat_version
=
rand
()
&
0xff
;
p_output
->
i_pat_version
=
rand
()
&
0xff
;
p_output
->
i_pmt_version
=
rand
()
&
0xff
;
p_output
->
i_pmt_version
=
rand
()
&
0xff
;
p_output
->
p_pat_section
=
NULL
;
p_output
->
p_pat_section
=
NULL
;
p_output
->
p_pmt_section
=
NULL
;
p_output
->
p_pmt_section
=
NULL
;
p_output
->
p_sdt_section
=
NULL
;
p_output
->
i_ref_timestamp
=
0
;
p_output
->
i_ref_timestamp
=
0
;
p_output
->
i_ref_wallclock
=
mdate
();
p_output
->
i_ref_wallclock
=
mdate
();
p_output
->
i_eit_last_table_id
=
0
;
p_output
->
i_maddr
=
i_maddr
;
p_output
->
i_maddr
=
i_maddr
;
p_output
->
i_port
=
i_port
;
p_output
->
i_port
=
i_port
;
...
...
util.c
View file @
58845e9b
...
@@ -142,3 +142,53 @@ void msleep( mtime_t delay )
...
@@ -142,3 +142,53 @@ void msleep( mtime_t delay )
nanosleep
(
&
ts_delay
,
NULL
);
nanosleep
(
&
ts_delay
,
NULL
);
}
}
/*****************************************************************************
* hexDump
*****************************************************************************/
void
hexDump
(
uint8_t
*
p_data
,
uint32_t
i_len
)
{
uint16_t
i
,
j
;
char
*
p_outline
;
char
*
p_hrdata
;
p_outline
=
malloc
(
69
);
p_hrdata
=
malloc
(
17
);
for
(
i
=
0
;
i
<
i_len
;
i
+=
16
)
{
sprintf
(
p_outline
,
"%03x: "
,
i
);
for
(
j
=
0
;
j
<
16
;
j
++
)
{
if
(
i
+
j
<
i_len
)
{
sprintf
(
&
p_outline
[
5
+
(
3
*
j
)],
"%02x "
,
p_data
[
i
+
j
]
);
if
(
p_data
[
i
+
j
]
>=
32
&&
p_data
[
i
+
j
]
<=
136
)
{
sprintf
(
&
p_hrdata
[
j
],
"%c"
,
p_data
[
i
+
j
]
);
}
else
{
sprintf
(
&
p_hrdata
[
j
],
"."
);
}
}
else
{
sprintf
(
&
p_outline
[
5
+
(
3
*
j
)],
" "
);
sprintf
(
&
p_hrdata
[
j
],
" "
);
}
}
sprintf
(
&
p_outline
[
53
],
"%16s"
,
p_hrdata
);
msg_Dbg
(
NULL
,
p_outline
);
}
free
(
p_hrdata
);
free
(
p_outline
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment