Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
胡斌
/
srs
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
winlin
2015-01-25 10:54:25 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
ea85ad2e20c8ba9583e688257971d770dff7c023
ea85ad2e
1 parent
d22e4e86
for #293, #250, move the ts codec to kernel ts.
隐藏空白字符变更
内嵌
并排对比
正在显示
7 个修改的文件
包含
829 行增加
和
800 行删除
trunk/src/app/srs_app_hls.cpp
trunk/src/app/srs_app_mpegts_udp.cpp
trunk/src/app/srs_app_mpegts_udp.hpp
trunk/src/kernel/srs_kernel_avc.cpp
trunk/src/kernel/srs_kernel_avc.hpp
trunk/src/kernel/srs_kernel_ts.cpp
trunk/src/kernel/srs_kernel_ts.hpp
trunk/src/app/srs_app_hls.cpp
查看文件 @
ea85ad2
...
...
@@ -50,6 +50,7 @@ using namespace std;
#include <srs_kernel_avc.hpp>
#include <srs_kernel_file.hpp>
#include <srs_rtmp_buffer.hpp>
#include <srs_kernel_ts.hpp>
// drop the segment when duration of ts too small.
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
...
...
trunk/src/app/srs_app_mpegts_udp.cpp
查看文件 @
ea85ad2
...
...
@@ -33,6 +33,9 @@ using namespace std;
#include <srs_kernel_log.hpp>
#include <srs_app_config.hpp>
// Transport Stream packets are 188 bytes in length.
#define TS_PACKET_SIZE 188
#ifdef SRS_AUTO_STREAM_CASTER
SrsMpegtsOverUdp
::
SrsMpegtsOverUdp
(
SrsConfDirective
*
c
)
...
...
@@ -51,10 +54,28 @@ int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf)
std
::
string
peer_ip
=
inet_ntoa
(
from
->
sin_addr
);
int
peer_port
=
ntohs
(
from
->
sin_port
);
// drop ts packet when size not modulus by 188
if
(
nb_buf
<
TS_PACKET_SIZE
||
(
nb_buf
%
TS_PACKET_SIZE
)
!=
0
)
{
srs_warn
(
"udp: drop %s:%d packet %d bytes"
,
peer_ip
.
c_str
(),
peer_port
,
nb_buf
);
return
ret
;
}
srs_info
(
"udp: got %s:%d packet %d bytes"
,
peer_ip
.
c_str
(),
peer_port
,
nb_buf
);
// TODO: FIXME: implements it.
// process each ts packet
for
(
int
i
=
0
;
i
<
nb_buf
;
i
+=
TS_PACKET_SIZE
)
{
char
*
ts_packet
=
buf
+
i
;
if
((
ret
=
on_ts_packet
(
ts_packet
))
!=
ERROR_SUCCESS
)
{
srs_warn
(
"mpegts: ignore ts packet error. ret=%d"
,
ret
);
continue
;
}
}
return
ret
;
}
int
SrsMpegtsOverUdp
::
on_ts_packet
(
char
*
ts_packet
)
{
int
ret
=
ERROR_SUCCESS
;
return
ret
;
}
...
...
trunk/src/app/srs_app_mpegts_udp.hpp
查看文件 @
ea85ad2
...
...
@@ -58,6 +58,11 @@ public:
* @remark user should never use the buf, for it's a shared memory bytes.
*/
virtual
int
on_udp_packet
(
sockaddr_in
*
from
,
char
*
buf
,
int
nb_buf
);
private
:
/**
* when got a ts packet, in size TS_PACKET_SIZE.
*/
virtual
int
on_ts_packet
(
char
*
ts_packet
);
};
#endif
...
...
trunk/src/kernel/srs_kernel_avc.cpp
查看文件 @
ea85ad2
...
...
@@ -32,702 +32,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using
namespace
std
;
// in ms, for HLS aac sync time.
#define SRS_CONF_DEFAULT_AAC_SYNC 100
// @see: ngx_rtmp_hls_audio
/* We assume here AAC frame size is 1024
* Need to handle AAC frames with frame size of 960 */
#define _SRS_AAC_SAMPLE_SIZE 1024
// the mpegts header specifed the video/audio pid.
#define TS_VIDEO_PID 256
#define TS_AUDIO_PID 257
// ts aac stream id.
#define TS_AUDIO_AAC 0xc0
// ts avc stream id.
#define TS_VIDEO_AVC 0xe0
/**
* the public data, event HLS disable, others can use it.
*/
// 0 = 5.5 kHz = 5512 Hz
// 1 = 11 kHz = 11025 Hz
// 2 = 22 kHz = 22050 Hz
// 3 = 44 kHz = 44100 Hz
int
flv_sample_rates
[]
=
{
5512
,
11025
,
22050
,
44100
};
// the sample rates in the codec,
// in the sequence header.
int
aac_sample_rates
[]
=
{
96000
,
88200
,
64000
,
48000
,
44100
,
32000
,
24000
,
22050
,
16000
,
12000
,
11025
,
8000
,
7350
,
0
,
0
,
0
};
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_AUTO_HLS_DELAY 63000
// @see: ngx_rtmp_mpegts_header
u_int8_t
mpegts_header
[]
=
{
/* TS */
0x47
,
0x40
,
0x00
,
0x10
,
0x00
,
/* PSI */
0x00
,
0xb0
,
0x0d
,
0x00
,
0x01
,
0xc1
,
0x00
,
0x00
,
/* PAT */
0x00
,
0x01
,
0xf0
,
0x01
,
/* CRC */
0x2e
,
0x70
,
0x19
,
0x05
,
/* stuffing 167 bytes */
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
/* TS */
0x47
,
0x50
,
0x01
,
0x10
,
0x00
,
/* PSI */
0x02
,
0xb0
,
0x17
,
0x00
,
0x01
,
0xc1
,
0x00
,
0x00
,
/* PMT */
0xe1
,
0x00
,
0xf0
,
0x00
,
// must generate header with/without video, @see:
// https://github.com/winlinvip/simple-rtmp-server/issues/40
0x1b
,
0xe1
,
0x00
,
0xf0
,
0x00
,
/* h264, pid=0x100=256 */
0x0f
,
0xe1
,
0x01
,
0xf0
,
0x00
,
/* aac, pid=0x101=257 */
/*0x03, 0xe1, 0x01, 0xf0, 0x00,*/
/* mp3 */
/* CRC */
0x2f
,
0x44
,
0xb9
,
0x9b
,
/* crc for aac */
/*0x4e, 0x59, 0x3d, 0x1e,*/
/* crc for mp3 */
/* stuffing 157 bytes */
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
};
// @see: ngx_rtmp_mpegts.c
// TODO: support full mpegts feature in future.
class
SrsMpegtsWriter
{
public
:
static
int
write_header
(
SrsFileWriter
*
writer
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
writer
->
write
(
mpegts_header
,
sizeof
(
mpegts_header
),
NULL
))
!=
ERROR_SUCCESS
)
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write ts file header failed. ret=%d"
,
ret
);
return
ret
;
}
return
ret
;
}
static
int
write_frame
(
SrsFileWriter
*
writer
,
SrsMpegtsFrame
*
frame
,
SrsSimpleBuffer
*
buffer
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
buffer
->
bytes
()
||
buffer
->
length
()
<=
0
)
{
return
ret
;
}
char
*
last
=
buffer
->
bytes
()
+
buffer
->
length
();
char
*
pos
=
buffer
->
bytes
();
bool
first
=
true
;
while
(
pos
<
last
)
{
static
char
packet
[
188
];
char
*
p
=
packet
;
frame
->
cc
++
;
// sync_byte; //8bits
*
p
++
=
0x47
;
// pid; //13bits
*
p
++
=
(
frame
->
pid
>>
8
)
&
0x1f
;
// payload_unit_start_indicator; //1bit
if
(
first
)
{
p
[
-
1
]
|=
0x40
;
}
*
p
++
=
frame
->
pid
;
// transport_scrambling_control; //2bits
// adaption_field_control; //2bits, 0x01: PayloadOnly
// continuity_counter; //4bits
*
p
++
=
0x10
|
(
frame
->
cc
&
0x0f
);
if
(
first
)
{
first
=
false
;
if
(
frame
->
key
)
{
p
[
-
1
]
|=
0x20
;
// Both Adaption and Payload
*
p
++
=
7
;
// size
*
p
++
=
0x50
;
// random access + PCR
p
=
write_pcr
(
p
,
frame
->
dts
-
SRS_AUTO_HLS_DELAY
);
}
// PES header
// packet_start_code_prefix; //24bits, '00 00 01'
*
p
++
=
0x00
;
*
p
++
=
0x00
;
*
p
++
=
0x01
;
//8bits
*
p
++
=
frame
->
sid
;
// pts(33bits) need 5bytes.
u_int8_t
header_size
=
5
;
u_int8_t
flags
=
0x80
;
// pts
// dts(33bits) need 5bytes also
if
(
frame
->
dts
!=
frame
->
pts
)
{
header_size
+=
5
;
flags
|=
0x40
;
// dts
}
// 3bytes: flag fields from PES_packet_length to PES_header_data_length
int
pes_size
=
(
last
-
pos
)
+
header_size
+
3
;
if
(
pes_size
>
0xffff
)
{
/**
* when actual packet length > 0xffff(65535),
* which exceed the max u_int16_t packet length,
* use 0 packet length, the next unit start indicates the end of packet.
*/
pes_size
=
0
;
}
// PES_packet_length; //16bits
*
p
++
=
(
pes_size
>>
8
);
*
p
++
=
pes_size
;
// PES_scrambling_control; //2bits, '10'
// PES_priority; //1bit
// data_alignment_indicator; //1bit
// copyright; //1bit
// original_or_copy; //1bit
*
p
++
=
0x80
;
/* H222 */
// PTS_DTS_flags; //2bits
// ESCR_flag; //1bit
// ES_rate_flag; //1bit
// DSM_trick_mode_flag; //1bit
// additional_copy_info_flag; //1bit
// PES_CRC_flag; //1bit
// PES_extension_flag; //1bit
*
p
++
=
flags
;
// PES_header_data_length; //8bits
*
p
++
=
header_size
;
// pts; // 33bits
p
=
write_pts
(
p
,
flags
>>
6
,
frame
->
pts
+
SRS_AUTO_HLS_DELAY
);
// dts; // 33bits
if
(
frame
->
dts
!=
frame
->
pts
)
{
p
=
write_pts
(
p
,
1
,
frame
->
dts
+
SRS_AUTO_HLS_DELAY
);
}
}
int
body_size
=
sizeof
(
packet
)
-
(
p
-
packet
);
int
in_size
=
last
-
pos
;
if
(
body_size
<=
in_size
)
{
memcpy
(
p
,
pos
,
body_size
);
pos
+=
body_size
;
}
else
{
p
=
fill_stuff
(
p
,
packet
,
body_size
,
in_size
);
memcpy
(
p
,
pos
,
in_size
);
pos
=
last
;
}
// write ts packet
if
((
ret
=
writer
->
write
(
packet
,
sizeof
(
packet
),
NULL
))
!=
ERROR_SUCCESS
)
{
if
(
!
srs_is_client_gracefully_close
(
ret
))
{
srs_error
(
"write ts file failed. ret=%d"
,
ret
);
}
return
ret
;
}
}
return
ret
;
}
private
:
static
char
*
fill_stuff
(
char
*
pes_body_end
,
char
*
packet
,
int
body_size
,
int
in_size
)
{
char
*
p
=
pes_body_end
;
// insert the stuff bytes before PES body
int
stuff_size
=
(
body_size
-
in_size
);
// adaption_field_control; //2bits
if
(
packet
[
3
]
&
0x20
)
{
// has adaptation
// packet[4]: adaption_field_length
// packet[5]: adaption field data
// base: start of PES body
char
*
base
=
&
packet
[
5
]
+
packet
[
4
];
int
len
=
p
-
base
;
p
=
(
char
*
)
memmove
(
base
+
stuff_size
,
base
,
len
)
+
len
;
// increase the adaption field size.
packet
[
4
]
+=
stuff_size
;
return
p
;
}
// create adaption field.
// adaption_field_control; //2bits
packet
[
3
]
|=
0x20
;
// base: start of PES body
char
*
base
=
&
packet
[
4
];
int
len
=
p
-
base
;
p
=
(
char
*
)
memmove
(
base
+
stuff_size
,
base
,
len
)
+
len
;
// adaption_field_length; //8bits
packet
[
4
]
=
(
stuff_size
-
1
);
if
(
stuff_size
>=
2
)
{
// adaption field flags.
packet
[
5
]
=
0
;
// adaption data.
if
(
stuff_size
>
2
)
{
memset
(
&
packet
[
6
],
0xff
,
stuff_size
-
2
);
}
}
return
p
;
}
static
char
*
write_pcr
(
char
*
p
,
int64_t
pcr
)
{
// the pcr=dts-delay
// and the pcr maybe negative
// @see https://github.com/winlinvip/simple-rtmp-server/issues/268
int64_t
v
=
srs_max
(
0
,
pcr
);
*
p
++
=
(
char
)
(
v
>>
25
);
*
p
++
=
(
char
)
(
v
>>
17
);
*
p
++
=
(
char
)
(
v
>>
9
);
*
p
++
=
(
char
)
(
v
>>
1
);
*
p
++
=
(
char
)
(
v
<<
7
|
0x7e
);
*
p
++
=
0
;
return
p
;
}
static
char
*
write_pts
(
char
*
p
,
u_int8_t
fb
,
int64_t
pts
)
{
int32_t
val
;
val
=
fb
<<
4
|
(((
pts
>>
30
)
&
0x07
)
<<
1
)
|
1
;
*
p
++
=
val
;
val
=
(((
pts
>>
15
)
&
0x7fff
)
<<
1
)
|
1
;
*
p
++
=
(
val
>>
8
);
*
p
++
=
val
;
val
=
(((
pts
)
&
0x7fff
)
<<
1
)
|
1
;
*
p
++
=
(
val
>>
8
);
*
p
++
=
val
;
return
p
;
}
};
SrsMpegtsFrame
::
SrsMpegtsFrame
()
{
pts
=
dts
=
0
;
pid
=
sid
=
cc
=
0
;
key
=
false
;
}
SrsTSMuxer
::
SrsTSMuxer
(
SrsFileWriter
*
w
)
{
writer
=
w
;
}
SrsTSMuxer
::~
SrsTSMuxer
()
{
close
();
}
int
SrsTSMuxer
::
open
(
string
_path
)
{
int
ret
=
ERROR_SUCCESS
;
path
=
_path
;
close
();
if
((
ret
=
writer
->
open
(
path
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// write mpegts header
if
((
ret
=
SrsMpegtsWriter
::
write_header
(
writer
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsTSMuxer
::
write_audio
(
SrsMpegtsFrame
*
af
,
SrsSimpleBuffer
*
ab
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
writer
,
af
,
ab
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsTSMuxer
::
write_video
(
SrsMpegtsFrame
*
vf
,
SrsSimpleBuffer
*
vb
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
writer
,
vf
,
vb
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
void
SrsTSMuxer
::
close
()
{
writer
->
close
();
}
SrsTsAacJitter
::
SrsTsAacJitter
()
{
base_pts
=
0
;
nb_samples
=
0
;
// TODO: config it, 0 means no adjust
sync_ms
=
SRS_CONF_DEFAULT_AAC_SYNC
;
}
SrsTsAacJitter
::~
SrsTsAacJitter
()
{
}
int64_t
SrsTsAacJitter
::
on_buffer_start
(
int64_t
flv_pts
,
int
sample_rate
,
int
aac_sample_rate
)
{
// use sample rate in flv/RTMP.
int
flv_sample_rate
=
flv_sample_rates
[
sample_rate
&
0x03
];
// override the sample rate by sequence header
if
(
aac_sample_rate
!=
__SRS_AAC_SAMPLE_RATE_UNSET
)
{
flv_sample_rate
=
aac_sample_rates
[
aac_sample_rate
];
}
// sync time set to 0, donot adjust the aac timestamp.
if
(
!
sync_ms
)
{
return
flv_pts
;
}
// @see: ngx_rtmp_hls_audio
// drop the rtmp audio packet timestamp, re-calc it by sample rate.
//
// resample for the tbn of ts is 90000, flv is 1000,
// we will lost timestamp if use audio packet timestamp,
// so we must resample. or audio will corupt in IOS.
int64_t
est_pts
=
base_pts
+
nb_samples
*
90000LL
*
_SRS_AAC_SAMPLE_SIZE
/
flv_sample_rate
;
int64_t
dpts
=
(
int64_t
)
(
est_pts
-
flv_pts
);
if
(
dpts
<=
(
int64_t
)
sync_ms
*
90
&&
dpts
>=
(
int64_t
)
sync_ms
*
-
90
)
{
srs_info
(
"HLS correct aac pts "
"from %"
PRId64
" to %"
PRId64
", base=%"
PRId64
", nb_samples=%d, sample_rate=%d"
,
flv_pts
,
est_pts
,
nb_samples
,
flv_sample_rate
,
base_pts
);
nb_samples
++
;
return
est_pts
;
}
// resync
srs_trace
(
"HLS aac resync, dpts=%"
PRId64
", pts=%"
PRId64
", base=%"
PRId64
", nb_samples=%"
PRId64
", sample_rate=%d"
,
dpts
,
flv_pts
,
base_pts
,
nb_samples
,
flv_sample_rate
);
base_pts
=
flv_pts
;
nb_samples
=
1
;
return
flv_pts
;
}
void
SrsTsAacJitter
::
on_buffer_continue
()
{
nb_samples
++
;
}
SrsTsCache
::
SrsTsCache
()
{
aac_jitter
=
new
SrsTsAacJitter
();
ab
=
new
SrsSimpleBuffer
();
vb
=
new
SrsSimpleBuffer
();
af
=
new
SrsMpegtsFrame
();
vf
=
new
SrsMpegtsFrame
();
}
SrsTsCache
::~
SrsTsCache
()
{
srs_freep
(
aac_jitter
);
ab
->
erase
(
ab
->
length
());
vb
->
erase
(
vb
->
length
());
srs_freep
(
ab
);
srs_freep
(
vb
);
srs_freep
(
af
);
srs_freep
(
vf
);
}
int
SrsTsCache
::
cache_audio
(
SrsAvcAacCodec
*
codec
,
int64_t
pts
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
// start buffer, set the af
if
(
ab
->
length
()
==
0
)
{
pts
=
aac_jitter
->
on_buffer_start
(
pts
,
sample
->
sound_rate
,
codec
->
aac_sample_rate
);
af
->
dts
=
af
->
pts
=
pts
;
af
->
pid
=
TS_AUDIO_PID
;
af
->
sid
=
TS_AUDIO_AAC
;
}
else
{
aac_jitter
->
on_buffer_continue
();
}
// write audio to cache.
if
((
ret
=
do_cache_audio
(
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsTsCache
::
cache_video
(
SrsAvcAacCodec
*
codec
,
int64_t
dts
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
// write video to cache.
if
((
ret
=
do_cache_video
(
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
vf
->
dts
=
dts
;
vf
->
pts
=
vf
->
dts
+
sample
->
cts
*
90
;
vf
->
pid
=
TS_VIDEO_PID
;
vf
->
sid
=
TS_VIDEO_AVC
;
vf
->
key
=
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
;
return
ret
;
}
int
SrsTsCache
::
do_cache_audio
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
for
(
int
i
=
0
;
i
<
sample
->
nb_sample_units
;
i
++
)
{
SrsCodecSampleUnit
*
sample_unit
=
&
sample
->
sample_units
[
i
];
int32_t
size
=
sample_unit
->
size
;
if
(
!
sample_unit
->
bytes
||
size
<=
0
||
size
>
0x1fff
)
{
ret
=
ERROR_HLS_AAC_FRAME_LENGTH
;
srs_error
(
"invalid aac frame length=%d, ret=%d"
,
size
,
ret
);
return
ret
;
}
// the frame length is the AAC raw data plus the adts header size.
int32_t
frame_length
=
size
+
7
;
// AAC-ADTS
// 6.2 Audio Data Transport Stream, ADTS
// in aac-iso-13818-7.pdf, page 26.
// fixed 7bytes header
static
u_int8_t
adts_header
[
7
]
=
{
0xff
,
0xf1
,
0x00
,
0x00
,
0x00
,
0x0f
,
0xfc
};
/*
// adts_fixed_header
// 2B, 16bits
int16_t syncword; //12bits, '1111 1111 1111'
int8_t ID; //1bit, '0'
int8_t layer; //2bits, '00'
int8_t protection_absent; //1bit, can be '1'
// 12bits
int8_t profile; //2bit, 7.1 Profiles, page 40
TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46
int8_t private_bit; //1bit, can be '0'
int8_t channel_configuration; //3bits, Table 8
int8_t original_or_copy; //1bit, can be '0'
int8_t home; //1bit, can be '0'
// adts_variable_header
// 28bits
int8_t copyright_identification_bit; //1bit, can be '0'
int8_t copyright_identification_start; //1bit, can be '0'
int16_t frame_length; //13bits
int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream.
int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block()
*/
// profile, 2bits
adts_header
[
2
]
=
(
codec
->
aac_profile
<<
6
)
&
0xc0
;
// sampling_frequency_index 4bits
adts_header
[
2
]
|=
(
codec
->
aac_sample_rate
<<
2
)
&
0x3c
;
// channel_configuration 3bits
adts_header
[
2
]
|=
(
codec
->
aac_channels
>>
2
)
&
0x01
;
adts_header
[
3
]
=
(
codec
->
aac_channels
<<
6
)
&
0xc0
;
// frame_length 13bits
adts_header
[
3
]
|=
(
frame_length
>>
11
)
&
0x03
;
adts_header
[
4
]
=
(
frame_length
>>
3
)
&
0xff
;
adts_header
[
5
]
=
((
frame_length
<<
5
)
&
0xe0
);
// adts_buffer_fullness; //11bits
adts_header
[
5
]
|=
0x1f
;
// copy to audio buffer
ab
->
append
((
const
char
*
)
adts_header
,
sizeof
(
adts_header
));
ab
->
append
(
sample_unit
->
bytes
,
sample_unit
->
size
);
}
return
ret
;
}
int
SrsTsCache
::
do_cache_video
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
// for type1/5/6, insert aud packet.
static
u_int8_t
aud_nal
[]
=
{
0x00
,
0x00
,
0x00
,
0x01
,
0x09
,
0xf0
};
bool
sps_pps_sent
=
false
;
bool
aud_sent
=
false
;
/**
* a ts sample is format as:
* 00 00 00 01 // header
* xxxxxxx // data bytes
* 00 00 01 // continue header
* xxxxxxx // data bytes.
* so, for each sample, we append header in aud_nal, then appends the bytes in sample.
*/
for
(
int
i
=
0
;
i
<
sample
->
nb_sample_units
;
i
++
)
{
SrsCodecSampleUnit
*
sample_unit
=
&
sample
->
sample_units
[
i
];
int32_t
size
=
sample_unit
->
size
;
if
(
!
sample_unit
->
bytes
||
size
<=
0
)
{
ret
=
ERROR_HLS_AVC_SAMPLE_SIZE
;
srs_error
(
"invalid avc sample length=%d, ret=%d"
,
size
,
ret
);
return
ret
;
}
/**
* step 1:
* first, before each "real" sample,
* we add some packets according to the nal_unit_type,
* for example, when got nal_unit_type=5, insert SPS/PPS before sample.
*/
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
u_int8_t
nal_unit_type
;
nal_unit_type
=
*
sample_unit
->
bytes
;
nal_unit_type
&=
0x1f
;
// @see: ngx_rtmp_hls_video
// Table 7-1 C NAL unit type codes, page 61
// 1: Coded slice
if
(
nal_unit_type
==
1
)
{
sps_pps_sent
=
false
;
}
// 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61
// @see: ngx_rtmp_hls_append_aud
if
(
!
aud_sent
)
{
// @remark, when got type 9, we donot send aud_nal, but it will make
// ios unhappy, so we remove it.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/281
/*if (nal_unit_type == 9) {
aud_sent = true;
}*/
if
(
nal_unit_type
==
1
||
nal_unit_type
==
5
||
nal_unit_type
==
6
)
{
// for type 6, append a aud with type 9.
vb
->
append
((
const
char
*
)
aud_nal
,
sizeof
(
aud_nal
));
aud_sent
=
true
;
}
}
// 5: Coded slice of an IDR picture.
// insert sps/pps before IDR or key frame is ok.
if
(
nal_unit_type
==
5
&&
!
sps_pps_sent
)
{
sps_pps_sent
=
true
;
// @see: ngx_rtmp_hls_append_sps_pps
if
(
codec
->
sequenceParameterSetLength
>
0
)
{
// AnnexB prefix, for sps always 4 bytes header
vb
->
append
((
const
char
*
)
aud_nal
,
4
);
// sps
vb
->
append
(
codec
->
sequenceParameterSetNALUnit
,
codec
->
sequenceParameterSetLength
);
}
if
(
codec
->
pictureParameterSetLength
>
0
)
{
// AnnexB prefix, for pps always 4 bytes header
vb
->
append
((
const
char
*
)
aud_nal
,
4
);
// pps
vb
->
append
(
codec
->
pictureParameterSetNALUnit
,
codec
->
pictureParameterSetLength
);
}
}
// 7-9, ignore, @see: ngx_rtmp_hls_video
if
(
nal_unit_type
>=
7
&&
nal_unit_type
<=
9
)
{
continue
;
}
/**
* step 2:
* output the "real" sample, in buf.
* when we output some special assist packets according to nal_unit_type
*/
// sample start prefix, '00 00 00 01' or '00 00 01'
u_int8_t
*
p
=
aud_nal
+
1
;
u_int8_t
*
end
=
p
+
3
;
// first AnnexB prefix is long (4 bytes)
if
(
vb
->
length
()
==
0
)
{
p
=
aud_nal
;
}
vb
->
append
((
const
char
*
)
p
,
end
-
p
);
// sample data
vb
->
append
(
sample_unit
->
bytes
,
sample_unit
->
size
);
}
return
ret
;
}
SrsCodecSampleUnit
::
SrsCodecSampleUnit
()
{
size
=
0
;
...
...
trunk/src/kernel/srs_kernel_avc.hpp
查看文件 @
ea85ad2
...
...
@@ -95,109 +95,6 @@ enum SrsCodecAudioSoundType
SrsCodecAudioSoundTypeStereo
=
1
,
};
// @see: ngx_rtmp_SrsMpegtsFrame_t
class
SrsMpegtsFrame
{
public
:
int64_t
pts
;
int64_t
dts
;
int
pid
;
int
sid
;
int
cc
;
bool
key
;
SrsMpegtsFrame
();
};
/**
* write data from frame(header info) and buffer(data) to ts file.
* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter
*/
class
SrsTSMuxer
{
private
:
SrsFileWriter
*
writer
;
std
::
string
path
;
public
:
SrsTSMuxer
(
SrsFileWriter
*
w
);
virtual
~
SrsTSMuxer
();
public
:
virtual
int
open
(
std
::
string
_path
);
virtual
int
write_audio
(
SrsMpegtsFrame
*
af
,
SrsSimpleBuffer
*
ab
);
virtual
int
write_video
(
SrsMpegtsFrame
*
vf
,
SrsSimpleBuffer
*
vb
);
virtual
void
close
();
};
/**
* jitter correct for audio,
* the sample rate 44100/32000 will lost precise,
* when mp4/ts(tbn=90000) covert to flv/rtmp(1000),
* so the Hls on ipad or iphone will corrupt,
* @see nginx-rtmp: est_pts
*/
class
SrsTsAacJitter
{
private
:
int64_t
base_pts
;
int64_t
nb_samples
;
int
sync_ms
;
public
:
SrsTsAacJitter
();
virtual
~
SrsTsAacJitter
();
/**
* when buffer start, calc the "correct" pts for ts,
* @param flv_pts, the flv pts calc from flv header timestamp,
* @param sample_rate, the sample rate in format(flv/RTMP packet header).
* @param aac_sample_rate, the sample rate in codec(sequence header).
* @return the calc correct pts.
*/
virtual
int64_t
on_buffer_start
(
int64_t
flv_pts
,
int
sample_rate
,
int
aac_sample_rate
);
/**
* when buffer continue, muxer donot write to file,
* the audio buffer continue grow and donot need a pts,
* for the ts audio PES packet only has one pts at the first time.
*/
virtual
void
on_buffer_continue
();
};
/**
* ts stream cache,
* use to cache ts stream.
*
* about the flv tbn problem:
* flv tbn is 1/1000, ts tbn is 1/90000,
* when timestamp convert to flv tbn, it will loose precise,
* so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter,
* we use a aac jitter to correct the audio pts.
*/
class
SrsTsCache
{
public
:
// current frame and buffer
SrsMpegtsFrame
*
af
;
SrsSimpleBuffer
*
ab
;
SrsMpegtsFrame
*
vf
;
SrsSimpleBuffer
*
vb
;
protected
:
// time jitter for aac
SrsTsAacJitter
*
aac_jitter
;
public
:
SrsTsCache
();
virtual
~
SrsTsCache
();
public
:
/**
* write audio to cache
*/
virtual
int
cache_audio
(
SrsAvcAacCodec
*
codec
,
int64_t
pts
,
SrsCodecSample
*
sample
);
/**
* write video to muxer.
*/
virtual
int
cache_video
(
SrsAvcAacCodec
*
codec
,
int64_t
dts
,
SrsCodecSample
*
sample
);
private
:
virtual
int
do_cache_audio
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
);
virtual
int
do_cache_video
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
);
};
/**
* the codec sample unit.
* for h.264 video packet, a NALU is a sample unit.
...
...
trunk/src/kernel/srs_kernel_ts.cpp
查看文件 @
ea85ad2
...
...
@@ -37,6 +37,703 @@ using namespace std;
#include <srs_kernel_file.hpp>
#include <srs_kernel_avc.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_utility.hpp>
// in ms, for HLS aac sync time.
#define SRS_CONF_DEFAULT_AAC_SYNC 100
// @see: ngx_rtmp_hls_audio
/* We assume here AAC frame size is 1024
* Need to handle AAC frames with frame size of 960 */
#define _SRS_AAC_SAMPLE_SIZE 1024
// the mpegts header specifed the video/audio pid.
#define TS_VIDEO_PID 256
#define TS_AUDIO_PID 257
// ts aac stream id.
#define TS_AUDIO_AAC 0xc0
// ts avc stream id.
#define TS_VIDEO_AVC 0xe0
/**
* the public data, event HLS disable, others can use it.
*/
// 0 = 5.5 kHz = 5512 Hz
// 1 = 11 kHz = 11025 Hz
// 2 = 22 kHz = 22050 Hz
// 3 = 44 kHz = 44100 Hz
int
flv_sample_rates
[]
=
{
5512
,
11025
,
22050
,
44100
};
// the sample rates in the codec,
// in the sequence header.
int
aac_sample_rates
[]
=
{
96000
,
88200
,
64000
,
48000
,
44100
,
32000
,
24000
,
22050
,
16000
,
12000
,
11025
,
8000
,
7350
,
0
,
0
,
0
};
// @see: NGX_RTMP_HLS_DELAY,
// 63000: 700ms, ts_tbn=90000
#define SRS_AUTO_HLS_DELAY 63000
// @see: ngx_rtmp_mpegts_header
u_int8_t
mpegts_header
[]
=
{
/* TS */
0x47
,
0x40
,
0x00
,
0x10
,
0x00
,
/* PSI */
0x00
,
0xb0
,
0x0d
,
0x00
,
0x01
,
0xc1
,
0x00
,
0x00
,
/* PAT */
0x00
,
0x01
,
0xf0
,
0x01
,
/* CRC */
0x2e
,
0x70
,
0x19
,
0x05
,
/* stuffing 167 bytes */
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
/* TS */
0x47
,
0x50
,
0x01
,
0x10
,
0x00
,
/* PSI */
0x02
,
0xb0
,
0x17
,
0x00
,
0x01
,
0xc1
,
0x00
,
0x00
,
/* PMT */
0xe1
,
0x00
,
0xf0
,
0x00
,
// must generate header with/without video, @see:
// https://github.com/winlinvip/simple-rtmp-server/issues/40
0x1b
,
0xe1
,
0x00
,
0xf0
,
0x00
,
/* h264, pid=0x100=256 */
0x0f
,
0xe1
,
0x01
,
0xf0
,
0x00
,
/* aac, pid=0x101=257 */
/*0x03, 0xe1, 0x01, 0xf0, 0x00,*/
/* mp3 */
/* CRC */
0x2f
,
0x44
,
0xb9
,
0x9b
,
/* crc for aac */
/*0x4e, 0x59, 0x3d, 0x1e,*/
/* crc for mp3 */
/* stuffing 157 bytes */
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
};
// @see: ngx_rtmp_mpegts.c
// TODO: support full mpegts feature in future.
class
SrsMpegtsWriter
{
public
:
static
int
write_header
(
SrsFileWriter
*
writer
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
writer
->
write
(
mpegts_header
,
sizeof
(
mpegts_header
),
NULL
))
!=
ERROR_SUCCESS
)
{
ret
=
ERROR_HLS_WRITE_FAILED
;
srs_error
(
"write ts file header failed. ret=%d"
,
ret
);
return
ret
;
}
return
ret
;
}
static
int
write_frame
(
SrsFileWriter
*
writer
,
SrsMpegtsFrame
*
frame
,
SrsSimpleBuffer
*
buffer
)
{
int
ret
=
ERROR_SUCCESS
;
if
(
!
buffer
->
bytes
()
||
buffer
->
length
()
<=
0
)
{
return
ret
;
}
char
*
last
=
buffer
->
bytes
()
+
buffer
->
length
();
char
*
pos
=
buffer
->
bytes
();
bool
first
=
true
;
while
(
pos
<
last
)
{
static
char
packet
[
188
];
char
*
p
=
packet
;
frame
->
cc
++
;
// sync_byte; //8bits
*
p
++
=
0x47
;
// pid; //13bits
*
p
++
=
(
frame
->
pid
>>
8
)
&
0x1f
;
// payload_unit_start_indicator; //1bit
if
(
first
)
{
p
[
-
1
]
|=
0x40
;
}
*
p
++
=
frame
->
pid
;
// transport_scrambling_control; //2bits
// adaption_field_control; //2bits, 0x01: PayloadOnly
// continuity_counter; //4bits
*
p
++
=
0x10
|
(
frame
->
cc
&
0x0f
);
if
(
first
)
{
first
=
false
;
if
(
frame
->
key
)
{
p
[
-
1
]
|=
0x20
;
// Both Adaption and Payload
*
p
++
=
7
;
// size
*
p
++
=
0x50
;
// random access + PCR
p
=
write_pcr
(
p
,
frame
->
dts
-
SRS_AUTO_HLS_DELAY
);
}
// PES header
// packet_start_code_prefix; //24bits, '00 00 01'
*
p
++
=
0x00
;
*
p
++
=
0x00
;
*
p
++
=
0x01
;
//8bits
*
p
++
=
frame
->
sid
;
// pts(33bits) need 5bytes.
u_int8_t
header_size
=
5
;
u_int8_t
flags
=
0x80
;
// pts
// dts(33bits) need 5bytes also
if
(
frame
->
dts
!=
frame
->
pts
)
{
header_size
+=
5
;
flags
|=
0x40
;
// dts
}
// 3bytes: flag fields from PES_packet_length to PES_header_data_length
int
pes_size
=
(
last
-
pos
)
+
header_size
+
3
;
if
(
pes_size
>
0xffff
)
{
/**
* when actual packet length > 0xffff(65535),
* which exceed the max u_int16_t packet length,
* use 0 packet length, the next unit start indicates the end of packet.
*/
pes_size
=
0
;
}
// PES_packet_length; //16bits
*
p
++
=
(
pes_size
>>
8
);
*
p
++
=
pes_size
;
// PES_scrambling_control; //2bits, '10'
// PES_priority; //1bit
// data_alignment_indicator; //1bit
// copyright; //1bit
// original_or_copy; //1bit
*
p
++
=
0x80
;
/* H222 */
// PTS_DTS_flags; //2bits
// ESCR_flag; //1bit
// ES_rate_flag; //1bit
// DSM_trick_mode_flag; //1bit
// additional_copy_info_flag; //1bit
// PES_CRC_flag; //1bit
// PES_extension_flag; //1bit
*
p
++
=
flags
;
// PES_header_data_length; //8bits
*
p
++
=
header_size
;
// pts; // 33bits
p
=
write_pts
(
p
,
flags
>>
6
,
frame
->
pts
+
SRS_AUTO_HLS_DELAY
);
// dts; // 33bits
if
(
frame
->
dts
!=
frame
->
pts
)
{
p
=
write_pts
(
p
,
1
,
frame
->
dts
+
SRS_AUTO_HLS_DELAY
);
}
}
int
body_size
=
sizeof
(
packet
)
-
(
p
-
packet
);
int
in_size
=
last
-
pos
;
if
(
body_size
<=
in_size
)
{
memcpy
(
p
,
pos
,
body_size
);
pos
+=
body_size
;
}
else
{
p
=
fill_stuff
(
p
,
packet
,
body_size
,
in_size
);
memcpy
(
p
,
pos
,
in_size
);
pos
=
last
;
}
// write ts packet
if
((
ret
=
writer
->
write
(
packet
,
sizeof
(
packet
),
NULL
))
!=
ERROR_SUCCESS
)
{
if
(
!
srs_is_client_gracefully_close
(
ret
))
{
srs_error
(
"write ts file failed. ret=%d"
,
ret
);
}
return
ret
;
}
}
return
ret
;
}
private
:
static
char
*
fill_stuff
(
char
*
pes_body_end
,
char
*
packet
,
int
body_size
,
int
in_size
)
{
char
*
p
=
pes_body_end
;
// insert the stuff bytes before PES body
int
stuff_size
=
(
body_size
-
in_size
);
// adaption_field_control; //2bits
if
(
packet
[
3
]
&
0x20
)
{
// has adaptation
// packet[4]: adaption_field_length
// packet[5]: adaption field data
// base: start of PES body
char
*
base
=
&
packet
[
5
]
+
packet
[
4
];
int
len
=
p
-
base
;
p
=
(
char
*
)
memmove
(
base
+
stuff_size
,
base
,
len
)
+
len
;
// increase the adaption field size.
packet
[
4
]
+=
stuff_size
;
return
p
;
}
// create adaption field.
// adaption_field_control; //2bits
packet
[
3
]
|=
0x20
;
// base: start of PES body
char
*
base
=
&
packet
[
4
];
int
len
=
p
-
base
;
p
=
(
char
*
)
memmove
(
base
+
stuff_size
,
base
,
len
)
+
len
;
// adaption_field_length; //8bits
packet
[
4
]
=
(
stuff_size
-
1
);
if
(
stuff_size
>=
2
)
{
// adaption field flags.
packet
[
5
]
=
0
;
// adaption data.
if
(
stuff_size
>
2
)
{
memset
(
&
packet
[
6
],
0xff
,
stuff_size
-
2
);
}
}
return
p
;
}
static
char
*
write_pcr
(
char
*
p
,
int64_t
pcr
)
{
// the pcr=dts-delay
// and the pcr maybe negative
// @see https://github.com/winlinvip/simple-rtmp-server/issues/268
int64_t
v
=
srs_max
(
0
,
pcr
);
*
p
++
=
(
char
)
(
v
>>
25
);
*
p
++
=
(
char
)
(
v
>>
17
);
*
p
++
=
(
char
)
(
v
>>
9
);
*
p
++
=
(
char
)
(
v
>>
1
);
*
p
++
=
(
char
)
(
v
<<
7
|
0x7e
);
*
p
++
=
0
;
return
p
;
}
static
char
*
write_pts
(
char
*
p
,
u_int8_t
fb
,
int64_t
pts
)
{
int32_t
val
;
val
=
fb
<<
4
|
(((
pts
>>
30
)
&
0x07
)
<<
1
)
|
1
;
*
p
++
=
val
;
val
=
(((
pts
>>
15
)
&
0x7fff
)
<<
1
)
|
1
;
*
p
++
=
(
val
>>
8
);
*
p
++
=
val
;
val
=
(((
pts
)
&
0x7fff
)
<<
1
)
|
1
;
*
p
++
=
(
val
>>
8
);
*
p
++
=
val
;
return
p
;
}
};
SrsMpegtsFrame
::
SrsMpegtsFrame
()
{
pts
=
dts
=
0
;
pid
=
sid
=
cc
=
0
;
key
=
false
;
}
SrsTSMuxer
::
SrsTSMuxer
(
SrsFileWriter
*
w
)
{
writer
=
w
;
}
SrsTSMuxer
::~
SrsTSMuxer
()
{
close
();
}
int
SrsTSMuxer
::
open
(
string
_path
)
{
int
ret
=
ERROR_SUCCESS
;
path
=
_path
;
close
();
if
((
ret
=
writer
->
open
(
path
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// write mpegts header
if
((
ret
=
SrsMpegtsWriter
::
write_header
(
writer
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsTSMuxer
::
write_audio
(
SrsMpegtsFrame
*
af
,
SrsSimpleBuffer
*
ab
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
writer
,
af
,
ab
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsTSMuxer
::
write_video
(
SrsMpegtsFrame
*
vf
,
SrsSimpleBuffer
*
vb
)
{
int
ret
=
ERROR_SUCCESS
;
if
((
ret
=
SrsMpegtsWriter
::
write_frame
(
writer
,
vf
,
vb
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
void
SrsTSMuxer
::
close
()
{
writer
->
close
();
}
SrsTsAacJitter
::
SrsTsAacJitter
()
{
base_pts
=
0
;
nb_samples
=
0
;
// TODO: config it, 0 means no adjust
sync_ms
=
SRS_CONF_DEFAULT_AAC_SYNC
;
}
SrsTsAacJitter
::~
SrsTsAacJitter
()
{
}
int64_t
SrsTsAacJitter
::
on_buffer_start
(
int64_t
flv_pts
,
int
sample_rate
,
int
aac_sample_rate
)
{
// use sample rate in flv/RTMP.
int
flv_sample_rate
=
flv_sample_rates
[
sample_rate
&
0x03
];
// override the sample rate by sequence header
if
(
aac_sample_rate
!=
__SRS_AAC_SAMPLE_RATE_UNSET
)
{
flv_sample_rate
=
aac_sample_rates
[
aac_sample_rate
];
}
// sync time set to 0, donot adjust the aac timestamp.
if
(
!
sync_ms
)
{
return
flv_pts
;
}
// @see: ngx_rtmp_hls_audio
// drop the rtmp audio packet timestamp, re-calc it by sample rate.
//
// resample for the tbn of ts is 90000, flv is 1000,
// we will lost timestamp if use audio packet timestamp,
// so we must resample. or audio will corupt in IOS.
int64_t
est_pts
=
base_pts
+
nb_samples
*
90000LL
*
_SRS_AAC_SAMPLE_SIZE
/
flv_sample_rate
;
int64_t
dpts
=
(
int64_t
)
(
est_pts
-
flv_pts
);
if
(
dpts
<=
(
int64_t
)
sync_ms
*
90
&&
dpts
>=
(
int64_t
)
sync_ms
*
-
90
)
{
srs_info
(
"HLS correct aac pts "
"from %"
PRId64
" to %"
PRId64
", base=%"
PRId64
", nb_samples=%d, sample_rate=%d"
,
flv_pts
,
est_pts
,
nb_samples
,
flv_sample_rate
,
base_pts
);
nb_samples
++
;
return
est_pts
;
}
// resync
srs_trace
(
"HLS aac resync, dpts=%"
PRId64
", pts=%"
PRId64
", base=%"
PRId64
", nb_samples=%"
PRId64
", sample_rate=%d"
,
dpts
,
flv_pts
,
base_pts
,
nb_samples
,
flv_sample_rate
);
base_pts
=
flv_pts
;
nb_samples
=
1
;
return
flv_pts
;
}
void
SrsTsAacJitter
::
on_buffer_continue
()
{
nb_samples
++
;
}
SrsTsCache
::
SrsTsCache
()
{
aac_jitter
=
new
SrsTsAacJitter
();
ab
=
new
SrsSimpleBuffer
();
vb
=
new
SrsSimpleBuffer
();
af
=
new
SrsMpegtsFrame
();
vf
=
new
SrsMpegtsFrame
();
}
SrsTsCache
::~
SrsTsCache
()
{
srs_freep
(
aac_jitter
);
ab
->
erase
(
ab
->
length
());
vb
->
erase
(
vb
->
length
());
srs_freep
(
ab
);
srs_freep
(
vb
);
srs_freep
(
af
);
srs_freep
(
vf
);
}
int
SrsTsCache
::
cache_audio
(
SrsAvcAacCodec
*
codec
,
int64_t
pts
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
// start buffer, set the af
if
(
ab
->
length
()
==
0
)
{
pts
=
aac_jitter
->
on_buffer_start
(
pts
,
sample
->
sound_rate
,
codec
->
aac_sample_rate
);
af
->
dts
=
af
->
pts
=
pts
;
af
->
pid
=
TS_AUDIO_PID
;
af
->
sid
=
TS_AUDIO_AAC
;
}
else
{
aac_jitter
->
on_buffer_continue
();
}
// write audio to cache.
if
((
ret
=
do_cache_audio
(
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
SrsTsCache
::
cache_video
(
SrsAvcAacCodec
*
codec
,
int64_t
dts
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
// write video to cache.
if
((
ret
=
do_cache_video
(
codec
,
sample
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
vf
->
dts
=
dts
;
vf
->
pts
=
vf
->
dts
+
sample
->
cts
*
90
;
vf
->
pid
=
TS_VIDEO_PID
;
vf
->
sid
=
TS_VIDEO_AVC
;
vf
->
key
=
sample
->
frame_type
==
SrsCodecVideoAVCFrameKeyFrame
;
return
ret
;
}
int
SrsTsCache
::
do_cache_audio
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
for
(
int
i
=
0
;
i
<
sample
->
nb_sample_units
;
i
++
)
{
SrsCodecSampleUnit
*
sample_unit
=
&
sample
->
sample_units
[
i
];
int32_t
size
=
sample_unit
->
size
;
if
(
!
sample_unit
->
bytes
||
size
<=
0
||
size
>
0x1fff
)
{
ret
=
ERROR_HLS_AAC_FRAME_LENGTH
;
srs_error
(
"invalid aac frame length=%d, ret=%d"
,
size
,
ret
);
return
ret
;
}
// the frame length is the AAC raw data plus the adts header size.
int32_t
frame_length
=
size
+
7
;
// AAC-ADTS
// 6.2 Audio Data Transport Stream, ADTS
// in aac-iso-13818-7.pdf, page 26.
// fixed 7bytes header
static
u_int8_t
adts_header
[
7
]
=
{
0xff
,
0xf1
,
0x00
,
0x00
,
0x00
,
0x0f
,
0xfc
};
/*
// adts_fixed_header
// 2B, 16bits
int16_t syncword; //12bits, '1111 1111 1111'
int8_t ID; //1bit, '0'
int8_t layer; //2bits, '00'
int8_t protection_absent; //1bit, can be '1'
// 12bits
int8_t profile; //2bit, 7.1 Profiles, page 40
TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46
int8_t private_bit; //1bit, can be '0'
int8_t channel_configuration; //3bits, Table 8
int8_t original_or_copy; //1bit, can be '0'
int8_t home; //1bit, can be '0'
// adts_variable_header
// 28bits
int8_t copyright_identification_bit; //1bit, can be '0'
int8_t copyright_identification_start; //1bit, can be '0'
int16_t frame_length; //13bits
int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream.
int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block()
*/
// profile, 2bits
adts_header
[
2
]
=
(
codec
->
aac_profile
<<
6
)
&
0xc0
;
// sampling_frequency_index 4bits
adts_header
[
2
]
|=
(
codec
->
aac_sample_rate
<<
2
)
&
0x3c
;
// channel_configuration 3bits
adts_header
[
2
]
|=
(
codec
->
aac_channels
>>
2
)
&
0x01
;
adts_header
[
3
]
=
(
codec
->
aac_channels
<<
6
)
&
0xc0
;
// frame_length 13bits
adts_header
[
3
]
|=
(
frame_length
>>
11
)
&
0x03
;
adts_header
[
4
]
=
(
frame_length
>>
3
)
&
0xff
;
adts_header
[
5
]
=
((
frame_length
<<
5
)
&
0xe0
);
// adts_buffer_fullness; //11bits
adts_header
[
5
]
|=
0x1f
;
// copy to audio buffer
ab
->
append
((
const
char
*
)
adts_header
,
sizeof
(
adts_header
));
ab
->
append
(
sample_unit
->
bytes
,
sample_unit
->
size
);
}
return
ret
;
}
int
SrsTsCache
::
do_cache_video
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
)
{
int
ret
=
ERROR_SUCCESS
;
// for type1/5/6, insert aud packet.
static
u_int8_t
aud_nal
[]
=
{
0x00
,
0x00
,
0x00
,
0x01
,
0x09
,
0xf0
};
bool
sps_pps_sent
=
false
;
bool
aud_sent
=
false
;
/**
* a ts sample is format as:
* 00 00 00 01 // header
* xxxxxxx // data bytes
* 00 00 01 // continue header
* xxxxxxx // data bytes.
* so, for each sample, we append header in aud_nal, then appends the bytes in sample.
*/
for
(
int
i
=
0
;
i
<
sample
->
nb_sample_units
;
i
++
)
{
SrsCodecSampleUnit
*
sample_unit
=
&
sample
->
sample_units
[
i
];
int32_t
size
=
sample_unit
->
size
;
if
(
!
sample_unit
->
bytes
||
size
<=
0
)
{
ret
=
ERROR_HLS_AVC_SAMPLE_SIZE
;
srs_error
(
"invalid avc sample length=%d, ret=%d"
,
size
,
ret
);
return
ret
;
}
/**
* step 1:
* first, before each "real" sample,
* we add some packets according to the nal_unit_type,
* for example, when got nal_unit_type=5, insert SPS/PPS before sample.
*/
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
u_int8_t
nal_unit_type
;
nal_unit_type
=
*
sample_unit
->
bytes
;
nal_unit_type
&=
0x1f
;
// @see: ngx_rtmp_hls_video
// Table 7-1 C NAL unit type codes, page 61
// 1: Coded slice
if
(
nal_unit_type
==
1
)
{
sps_pps_sent
=
false
;
}
// 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61
// @see: ngx_rtmp_hls_append_aud
if
(
!
aud_sent
)
{
// @remark, when got type 9, we donot send aud_nal, but it will make
// ios unhappy, so we remove it.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/281
/*if (nal_unit_type == 9) {
aud_sent = true;
}*/
if
(
nal_unit_type
==
1
||
nal_unit_type
==
5
||
nal_unit_type
==
6
)
{
// for type 6, append a aud with type 9.
vb
->
append
((
const
char
*
)
aud_nal
,
sizeof
(
aud_nal
));
aud_sent
=
true
;
}
}
// 5: Coded slice of an IDR picture.
// insert sps/pps before IDR or key frame is ok.
if
(
nal_unit_type
==
5
&&
!
sps_pps_sent
)
{
sps_pps_sent
=
true
;
// @see: ngx_rtmp_hls_append_sps_pps
if
(
codec
->
sequenceParameterSetLength
>
0
)
{
// AnnexB prefix, for sps always 4 bytes header
vb
->
append
((
const
char
*
)
aud_nal
,
4
);
// sps
vb
->
append
(
codec
->
sequenceParameterSetNALUnit
,
codec
->
sequenceParameterSetLength
);
}
if
(
codec
->
pictureParameterSetLength
>
0
)
{
// AnnexB prefix, for pps always 4 bytes header
vb
->
append
((
const
char
*
)
aud_nal
,
4
);
// pps
vb
->
append
(
codec
->
pictureParameterSetNALUnit
,
codec
->
pictureParameterSetLength
);
}
}
// 7-9, ignore, @see: ngx_rtmp_hls_video
if
(
nal_unit_type
>=
7
&&
nal_unit_type
<=
9
)
{
continue
;
}
/**
* step 2:
* output the "real" sample, in buf.
* when we output some special assist packets according to nal_unit_type
*/
// sample start prefix, '00 00 00 01' or '00 00 01'
u_int8_t
*
p
=
aud_nal
+
1
;
u_int8_t
*
end
=
p
+
3
;
// first AnnexB prefix is long (4 bytes)
if
(
vb
->
length
()
==
0
)
{
p
=
aud_nal
;
}
vb
->
append
((
const
char
*
)
p
,
end
-
p
);
// sample data
vb
->
append
(
sample_unit
->
bytes
,
sample_unit
->
size
);
}
return
ret
;
}
SrsTsEncoder
::
SrsTsEncoder
()
{
...
...
trunk/src/kernel/srs_kernel_ts.hpp
查看文件 @
ea85ad2
...
...
@@ -37,6 +37,110 @@ class SrsFileWriter;
class
SrsFileReader
;
class
SrsAvcAacCodec
;
class
SrsCodecSample
;
class
SrsSimpleBuffer
;
// @see: ngx_rtmp_SrsMpegtsFrame_t
class
SrsMpegtsFrame
{
public
:
int64_t
pts
;
int64_t
dts
;
int
pid
;
int
sid
;
int
cc
;
bool
key
;
SrsMpegtsFrame
();
};
/**
* write data from frame(header info) and buffer(data) to ts file.
* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter
*/
class
SrsTSMuxer
{
private
:
SrsFileWriter
*
writer
;
std
::
string
path
;
public
:
SrsTSMuxer
(
SrsFileWriter
*
w
);
virtual
~
SrsTSMuxer
();
public
:
virtual
int
open
(
std
::
string
_path
);
virtual
int
write_audio
(
SrsMpegtsFrame
*
af
,
SrsSimpleBuffer
*
ab
);
virtual
int
write_video
(
SrsMpegtsFrame
*
vf
,
SrsSimpleBuffer
*
vb
);
virtual
void
close
();
};
/**
* jitter correct for audio,
* the sample rate 44100/32000 will lost precise,
* when mp4/ts(tbn=90000) covert to flv/rtmp(1000),
* so the Hls on ipad or iphone will corrupt,
* @see nginx-rtmp: est_pts
*/
class
SrsTsAacJitter
{
private
:
int64_t
base_pts
;
int64_t
nb_samples
;
int
sync_ms
;
public
:
SrsTsAacJitter
();
virtual
~
SrsTsAacJitter
();
/**
* when buffer start, calc the "correct" pts for ts,
* @param flv_pts, the flv pts calc from flv header timestamp,
* @param sample_rate, the sample rate in format(flv/RTMP packet header).
* @param aac_sample_rate, the sample rate in codec(sequence header).
* @return the calc correct pts.
*/
virtual
int64_t
on_buffer_start
(
int64_t
flv_pts
,
int
sample_rate
,
int
aac_sample_rate
);
/**
* when buffer continue, muxer donot write to file,
* the audio buffer continue grow and donot need a pts,
* for the ts audio PES packet only has one pts at the first time.
*/
virtual
void
on_buffer_continue
();
};
/**
* ts stream cache,
* use to cache ts stream.
*
* about the flv tbn problem:
* flv tbn is 1/1000, ts tbn is 1/90000,
* when timestamp convert to flv tbn, it will loose precise,
* so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter,
* we use a aac jitter to correct the audio pts.
*/
class
SrsTsCache
{
public
:
// current frame and buffer
SrsMpegtsFrame
*
af
;
SrsSimpleBuffer
*
ab
;
SrsMpegtsFrame
*
vf
;
SrsSimpleBuffer
*
vb
;
protected
:
// time jitter for aac
SrsTsAacJitter
*
aac_jitter
;
public
:
SrsTsCache
();
virtual
~
SrsTsCache
();
public
:
/**
* write audio to cache
*/
virtual
int
cache_audio
(
SrsAvcAacCodec
*
codec
,
int64_t
pts
,
SrsCodecSample
*
sample
);
/**
* write video to muxer.
*/
virtual
int
cache_video
(
SrsAvcAacCodec
*
codec
,
int64_t
dts
,
SrsCodecSample
*
sample
);
private
:
virtual
int
do_cache_audio
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
);
virtual
int
do_cache_video
(
SrsAvcAacCodec
*
codec
,
SrsCodecSample
*
sample
);
};
/**
* encode data to ts file.
...
...
请
注册
或
登录
后发表评论