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
2014-11-21 14:26:47 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
2a9bec3d89d38dd1af9f420301526e47075fe559
2a9bec3d
1 parent
aa69f619
adjust the api, move audio and h264 after rtmp
隐藏空白字符变更
内嵌
并排对比
正在显示
2 个修改的文件
包含
693 行增加
和
693 行删除
trunk/src/libs/srs_librtmp.cpp
trunk/src/libs/srs_librtmp.hpp
trunk/src/libs/srs_librtmp.cpp
查看文件 @
2a9bec3
...
...
@@ -858,231 +858,622 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char*
return
ret
;
}
struct
FlvContext
{
SrsFileReader
reader
;
SrsFileWriter
writer
;
SrsFlvEncoder
enc
;
SrsFlvDecoder
dec
;
};
/**
* write audio raw frame to SRS.
*/
int
srs_audio_write_raw_frame
(
srs_rtmp_t
rtmp
,
char
sound_format
,
char
sound_rate
,
char
sound_size
,
char
sound_type
,
char
aac_packet_type
,
char
*
frame
,
int
frame_size
,
u_int32_t
timestamp
)
{
Context
*
context
=
(
Context
*
)
rtmp
;
srs_assert
(
context
);
srs_flv_t
srs_flv_open_read
(
const
char
*
file
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
flv
=
new
FlvContext
();
if
((
ret
=
flv
->
reader
.
open
(
file
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
}
// TODO: FIXME: for aac, must send the sequence header first.
if
((
ret
=
flv
->
dec
.
initialize
(
&
flv
->
reader
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
// for audio frame, there is 1 or 2 bytes header:
// 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
// 1bytes, AACPacketType for SoundFormat == 10
int
size
=
frame_size
+
1
;
if
(
aac_packet_type
==
SrsCodecAudioAAC
)
{
size
+=
1
;
}
char
*
data
=
new
char
[
size
];
char
*
p
=
data
;
return
flv
;
}
srs_flv_t
srs_flv_open_write
(
const
char
*
file
)
{
int
ret
=
ERROR_SUCCESS
;
u_int8_t
audio_header
=
sound_type
&
0x01
;
audio_header
|=
(
sound_size
<<
1
)
&
0x02
;
audio_header
|=
(
sound_rate
<<
2
)
&
0x0c
;
audio_header
|=
(
sound_format
<<
4
)
&
0xf0
;
FlvContext
*
flv
=
new
FlvContext
()
;
*
p
++
=
audio_header
;
if
((
ret
=
flv
->
writer
.
open
(
file
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
if
(
aac_packet_type
==
SrsCodecAudioAAC
)
{
*
p
++
=
aac_packet_type
;
}
if
((
ret
=
flv
->
enc
.
initialize
(
&
flv
->
writer
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
}
memcpy
(
p
,
frame
,
frame_size
);
return
flv
;
}
void
srs_flv_close
(
srs_flv_t
flv
)
{
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
srs_freep
(
context
);
return
srs_rtmp_write_packet
(
context
,
SRS_RTMP_TYPE_AUDIO
,
timestamp
,
data
,
size
);
}
int
srs_flv_read_header
(
srs_flv_t
flv
,
char
header
[
9
])
{
int
ret
=
ERROR_SUCCESS
;
/**
* write h264 packet, with rtmp header.
* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame.
* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU.
* @param h264_raw_data the h.264 raw data, user must free it.
*/
int
__srs_write_h264_packet
(
Context
*
context
,
int8_t
frame_type
,
int8_t
avc_packet_type
,
char
*
h264_raw_data
,
int
h264_raw_size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
// the timestamp in rtmp message header is dts.
u_int32_t
timestamp
=
dts
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
reader
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
// for h264 in RTMP video payload, there is 5bytes header:
// 1bytes, FrameType | CodecID
// 1bytes, AVCPacketType
// 3bytes, CompositionTime, the cts.
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
int
size
=
h264_raw_size
+
5
;
char
*
data
=
new
char
[
size
];
char
*
p
=
data
;
if
((
ret
=
context
->
dec
.
read_header
(
header
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
// Frame Type, Type of video frame.
// CodecID, Codec Identifier.
// set the rtmp header
*
p
++
=
(
frame_type
<<
4
)
|
SrsCodecVideoAVC
;
char
ts
[
4
];
// tag size
if
((
ret
=
context
->
dec
.
read_previous_tag_size
(
ts
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// AVCPacketType
*
p
++
=
avc_packet_type
;
// CompositionTime
// pts = dts + cts, or
// cts = pts - dts.
// where cts is the header in rtmp video packet payload header.
u_int32_t
cts
=
pts
-
dts
;
char
*
pp
=
(
char
*
)
&
cts
;
*
p
++
=
pp
[
2
];
*
p
++
=
pp
[
1
];
*
p
++
=
pp
[
0
];
return
ret
;
// h.264 raw data.
memcpy
(
p
,
h264_raw_data
,
h264_raw_size
);
return
srs_rtmp_write_packet
(
context
,
SRS_RTMP_TYPE_VIDEO
,
timestamp
,
data
,
size
);
}
int
srs_flv_read_tag_header
(
srs_flv_t
flv
,
char
*
ptype
,
int32_t
*
pdata_size
,
u_int32_t
*
ptime
)
/**
* write the h264 sps/pps in context over RTMP.
*/
int
__srs_write_h264_sps_pps
(
Context
*
context
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
reader
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
// only send when both sps and pps changed.
if
(
!
context
->
h264_sps_changed
||
!
context
->
h264_pps_changed
)
{
return
ret
;
}
if
((
ret
=
context
->
dec
.
read_tag_header
(
ptype
,
pdata_size
,
ptime
))
!=
ERROR_SUCCESS
)
{
// 5bytes sps/pps header:
// configurationVersion, AVCProfileIndication, profile_compatibility,
// AVCLevelIndication, lengthSizeMinusOne
// 3bytes size of sps:
// numOfSequenceParameterSets, sequenceParameterSetLength(2B)
// Nbytes of sps.
// sequenceParameterSetNALUnit
// 3bytes size of pps:
// numOfPictureParameterSets, pictureParameterSetLength
// Nbytes of pps:
// pictureParameterSetNALUnit
int
nb_packet
=
5
+
3
+
(
int
)
context
->
h264_sps
.
length
()
+
3
+
(
int
)
context
->
h264_pps
.
length
();
char
*
packet
=
new
char
[
nb_packet
];
SrsAutoFree
(
char
,
packet
);
// use stream to generate the h264 packet.
SrsStream
stream
;
if
((
ret
=
stream
.
initialize
(
packet
,
nb_packet
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
srs_flv_read_tag_data
(
srs_flv_t
flv
,
char
*
data
,
int32_t
size
)
{
int
ret
=
ERROR_SUCCESS
;
// decode the SPS:
// @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
if
(
true
)
{
srs_assert
((
int
)
context
->
h264_sps
.
length
()
>=
4
);
char
*
frame
=
(
char
*
)
context
->
h264_sps
.
data
();
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
reader
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
// @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205
// Baseline profile profile_idc is 66(0x42).
// Main profile profile_idc is 77(0x4d).
// Extended profile profile_idc is 88(0x58).
u_int8_t
profile_idc
=
frame
[
1
];
//u_int8_t constraint_set = frame[2];
u_int8_t
level_idc
=
frame
[
3
];
// generate the sps/pps header
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// configurationVersion
stream
.
write_1bytes
(
0x01
);
// AVCProfileIndication
stream
.
write_1bytes
(
profile_idc
);
// profile_compatibility
stream
.
write_1bytes
(
0x00
);
// AVCLevelIndication
stream
.
write_1bytes
(
level_idc
);
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size,
// so we always set it to 0x03.
stream
.
write_1bytes
(
0x03
);
}
if
((
ret
=
context
->
dec
.
read_tag_data
(
data
,
size
))
!=
ERROR_SUCCESS
)
{
return
ret
;
// sps
if
(
true
)
{
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// numOfSequenceParameterSets, always 1
stream
.
write_1bytes
(
0x01
);
// sequenceParameterSetLength
stream
.
write_2bytes
(
context
->
h264_sps
.
length
());
// sequenceParameterSetNALUnit
stream
.
write_string
(
context
->
h264_sps
);
}
char
ts
[
4
];
// tag size
if
((
ret
=
context
->
dec
.
read_previous_tag_size
(
ts
))
!=
ERROR_SUCCESS
)
{
return
ret
;
// pps
if
(
true
)
{
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// numOfPictureParameterSets, always 1
stream
.
write_1bytes
(
0x01
);
// pictureParameterSetLength
stream
.
write_2bytes
(
context
->
h264_pps
.
length
());
// pictureParameterSetNALUnit
stream
.
write_string
(
context
->
h264_pps
);
}
return
ret
;
}
int
srs_flv_write_header
(
srs_flv_t
flv
,
char
header
[
9
])
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
writer
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
// reset sps and pps.
context
->
h264_sps_changed
=
false
;
context
->
h264_pps_changed
=
false
;
context
->
h264_sps_pps_sent
=
true
;
if
((
ret
=
context
->
enc
.
write_header
(
header
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// TODO: FIXME: for more profile.
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144
return
ret
;
// send out h264 packet.
int8_t
frame_type
=
SrsCodecVideoAVCFrameKeyFrame
;
int8_t
avc_packet_type
=
SrsCodecVideoAVCTypeSequenceHeader
;
return
__srs_write_h264_packet
(
context
,
frame_type
,
avc_packet_type
,
packet
,
nb_packet
,
dts
,
pts
);
}
int
srs_flv_write_tag
(
srs_flv_t
flv
,
char
type
,
int32_t
time
,
char
*
data
,
int
size
)
{
/**
* write h264 IPB-frame.
*/
int
__srs_write_h264_ipb_frame
(
Context
*
context
,
char
*
data
,
int
size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
// when sps or pps not sent, ignore the packet.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
if
(
!
context
->
h264_sps_pps_sent
)
{
return
ERROR_H264_DROP_BEFORE_SPS_PPS
;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
u_int8_t
nal_unit_type
=
(
char
)
data
[
0
]
&
0x1f
;
// 4bytes size of nalu:
// NALUnitLength
// Nbytes of nalu.
// NALUnit
int
nb_packet
=
4
+
size
;
char
*
packet
=
new
char
[
nb_packet
];
SrsAutoFree
(
char
,
packet
);
// use stream to generate the h264 packet.
SrsStream
stream
;
if
((
ret
=
stream
.
initialize
(
packet
,
nb_packet
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
if
(
!
context
->
writer
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size
u_int32_t
NAL_unit_length
=
size
;
// mux the avc NALU in "ISO Base Media File Format"
// from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
// NALUnitLength
stream
.
write_4bytes
(
NAL_unit_length
);
// NALUnit
stream
.
write_bytes
(
data
,
size
);
// send out h264 packet.
int8_t
frame_type
=
SrsCodecVideoAVCFrameInterFrame
;
if
(
nal_unit_type
!=
1
)
{
frame_type
=
SrsCodecVideoAVCFrameKeyFrame
;
}
int8_t
avc_packet_type
=
SrsCodecVideoAVCTypeNALU
;
return
__srs_write_h264_packet
(
context
,
frame_type
,
avc_packet_type
,
packet
,
nb_packet
,
dts
,
pts
);
if
(
type
==
SRS_RTMP_TYPE_AUDIO
)
{
return
context
->
enc
.
write_audio
(
time
,
data
,
size
);
}
else
if
(
type
==
SRS_RTMP_TYPE_VIDEO
)
{
return
context
->
enc
.
write_video
(
time
,
data
,
size
);
return
ret
;
}
/**
* write h264 raw frame, maybe sps/pps/IPB-frame.
*/
int
__srs_write_h264_raw_frame
(
Context
*
context
,
char
*
frame
,
int
frame_size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
// ignore invalid frame,
// atleast 1bytes for SPS to decode the type
if
(
frame_size
<
1
)
{
return
ret
;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
u_int8_t
nal_unit_type
=
(
char
)
frame
[
0
]
&
0x1f
;
if
(
nal_unit_type
==
7
)
{
// atleast 1bytes for SPS to decode the type, profile, constrain and level.
if
(
frame_size
<
4
)
{
return
ret
;
}
std
::
string
sps
;
sps
.
append
(
frame
,
frame_size
);
if
(
context
->
h264_sps
==
sps
)
{
return
ERROR_H264_DUPLICATED_SPS
;
}
context
->
h264_sps_changed
=
true
;
context
->
h264_sps
=
sps
;
return
__srs_write_h264_sps_pps
(
context
,
dts
,
pts
);
}
else
if
(
nal_unit_type
==
8
)
{
std
::
string
pps
;
pps
.
append
(
frame
,
frame_size
);
if
(
context
->
h264_pps
==
pps
)
{
return
ERROR_H264_DUPLICATED_PPS
;
}
context
->
h264_pps_changed
=
true
;
context
->
h264_pps
=
pps
;
return
__srs_write_h264_sps_pps
(
context
,
dts
,
pts
);
}
else
{
return
context
->
enc
.
write_metadata
(
data
,
size
);
return
__srs_write_h264_ipb_frame
(
context
,
frame
,
frame_size
,
dts
,
pts
);
}
return
ret
;
}
int
srs_flv_size_tag
(
int
data_size
)
{
return
SrsFlvEncoder
::
size_tag
(
data_size
);
/**
* write h264 multiple frames, in annexb format.
*/
int
srs_h264_write_raw_frames
(
srs_rtmp_t
rtmp
,
char
*
frames
,
int
frames_size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
frames
!=
NULL
);
srs_assert
(
frames_size
>
0
);
srs_assert
(
rtmp
!=
NULL
);
Context
*
context
=
(
Context
*
)
rtmp
;
if
((
ret
=
context
->
h264_raw_stream
.
initialize
(
frames
,
frames_size
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// use the last error
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
// @see https://github.com/winlinvip/simple-rtmp-server/issues/204
int
error_code_return
=
ret
;
// send each frame.
while
(
!
context
->
h264_raw_stream
.
empty
())
{
// each frame must prefixed by annexb format.
// about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
int
pnb_start_code
=
0
;
if
(
!
srs_avc_startswith_annexb
(
&
context
->
h264_raw_stream
,
&
pnb_start_code
))
{
return
ERROR_H264_API_NO_PREFIXED
;
}
int
start
=
context
->
h264_raw_stream
.
pos
()
+
pnb_start_code
;
// find the last frame prefixed by annexb format.
context
->
h264_raw_stream
.
skip
(
pnb_start_code
);
while
(
!
context
->
h264_raw_stream
.
empty
())
{
if
(
srs_avc_startswith_annexb
(
&
context
->
h264_raw_stream
,
NULL
))
{
break
;
}
context
->
h264_raw_stream
.
skip
(
1
);
}
int
size
=
context
->
h264_raw_stream
.
pos
()
-
start
;
// send out the frame.
char
*
frame
=
context
->
h264_raw_stream
.
data
()
+
start
;
// it may be return error, but we must process all packets.
if
((
ret
=
__srs_write_h264_raw_frame
(
context
,
frame
,
size
,
dts
,
pts
))
!=
ERROR_SUCCESS
)
{
error_code_return
=
ret
;
// ignore known error, process all packets.
if
(
srs_h264_is_dvbsp_error
(
ret
)
||
srs_h264_is_duplicated_sps_error
(
ret
)
||
srs_h264_is_duplicated_pps_error
(
ret
)
)
{
continue
;
}
return
ret
;
}
}
return
error_code_return
;
}
int64_t
srs_flv_tellg
(
srs_flv_t
flv
)
srs_h264_bool
srs_h264_is_dvbsp_error
(
int
error_code
)
{
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
return
context
->
reader
.
tellg
();
return
error_code
==
ERROR_H264_DROP_BEFORE_SPS_PPS
;
}
void
srs_flv_lseek
(
srs_flv_t
flv
,
int64_t
offset
)
srs_h264_bool
srs_h264_is_duplicated_sps_error
(
int
error_code
)
{
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
context
->
reader
.
lseek
(
offset
);
return
error_code
==
ERROR_H264_DUPLICATED_SPS
;
}
srs_
flv_bool
srs_flv_is_eof
(
int
error_code
)
srs_
h264_bool
srs_h264_is_duplicated_pps_error
(
int
error_code
)
{
return
error_code
==
ERROR_
SYSTEM_FILE_EOF
;
return
error_code
==
ERROR_
H264_DUPLICATED_PPS
;
}
srs_flv_bool
srs_flv_is_sequence_header
(
char
*
data
,
int32_t
siz
e
)
int
srs_h264_startswith_annexb
(
char
*
h264_raw_data
,
int
h264_raw_size
,
int
*
pnb_start_cod
e
)
{
return
SrsFlvCodec
::
video_is_sequence_header
(
data
,
(
int
)
size
);
SrsStream
stream
;
if
(
stream
.
initialize
(
h264_raw_data
,
h264_raw_size
)
!=
ERROR_SUCCESS
)
{
return
false
;
}
return
srs_avc_startswith_annexb
(
&
stream
,
pnb_start_code
);
}
s
rs_flv_bool
srs_flv_is_keyframe
(
char
*
data
,
int32_t
size
)
s
truct
FlvContext
{
return
SrsFlvCodec
::
video_is_keyframe
(
data
,
(
int
)
size
);
}
SrsFileReader
reader
;
SrsFileWriter
writer
;
SrsFlvEncoder
enc
;
SrsFlvDecoder
dec
;
};
srs_
amf0_t
srs_amf0_parse
(
char
*
data
,
int
size
,
int
*
nparsed
)
srs_
flv_t
srs_flv_open_read
(
const
char
*
file
)
{
int
ret
=
ERROR_SUCCESS
;
srs_amf0_t
amf0
=
NULL
;
FlvContext
*
flv
=
new
FlvContext
()
;
SrsStream
stream
;
if
((
ret
=
stream
.
initialize
(
data
,
size
))
!=
ERROR_SUCCESS
)
{
return
amf0
;
if
((
ret
=
flv
->
reader
.
open
(
file
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
}
SrsAmf0Any
*
any
=
NULL
;
if
((
ret
=
SrsAmf0Any
::
discovery
(
&
stream
,
&
any
))
!=
ERROR_SUCCESS
)
{
return
amf0
;
if
((
ret
=
flv
->
dec
.
initialize
(
&
flv
->
reader
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
}
stream
.
skip
(
-
1
*
stream
.
pos
());
if
((
ret
=
any
->
read
(
&
stream
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
any
);
return
amf0
;
return
flv
;
}
srs_flv_t
srs_flv_open_write
(
const
char
*
file
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
flv
=
new
FlvContext
();
if
((
ret
=
flv
->
writer
.
open
(
file
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
}
if
(
nparsed
)
{
*
nparsed
=
stream
.
pos
();
if
((
ret
=
flv
->
enc
.
initialize
(
&
flv
->
writer
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
flv
);
return
NULL
;
}
amf0
=
(
srs_amf0_t
)
any
;
return
amf0
;
return
flv
;
}
srs_amf0_t
srs_amf0_create_number
(
srs_amf0_number
value
)
void
srs_flv_close
(
srs_flv_t
flv
)
{
return
SrsAmf0Any
::
number
(
value
);
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
srs_freep
(
context
);
}
srs_amf0_t
srs_amf0_create_ecma_array
(
)
int
srs_flv_read_header
(
srs_flv_t
flv
,
char
header
[
9
]
)
{
return
SrsAmf0Any
::
ecma_array
();
}
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
srs_amf0_t
srs_amf0_create_strict_array
()
{
if
(
!
context
->
reader
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
if
((
ret
=
context
->
dec
.
read_header
(
header
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
char
ts
[
4
];
// tag size
if
((
ret
=
context
->
dec
.
read_previous_tag_size
(
ts
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
srs_flv_read_tag_header
(
srs_flv_t
flv
,
char
*
ptype
,
int32_t
*
pdata_size
,
u_int32_t
*
ptime
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
reader
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
if
((
ret
=
context
->
dec
.
read_tag_header
(
ptype
,
pdata_size
,
ptime
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
srs_flv_read_tag_data
(
srs_flv_t
flv
,
char
*
data
,
int32_t
size
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
reader
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
if
((
ret
=
context
->
dec
.
read_tag_data
(
data
,
size
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
char
ts
[
4
];
// tag size
if
((
ret
=
context
->
dec
.
read_previous_tag_size
(
ts
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
srs_flv_write_header
(
srs_flv_t
flv
,
char
header
[
9
])
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
writer
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
if
((
ret
=
context
->
enc
.
write_header
(
header
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
return
ret
;
}
int
srs_flv_write_tag
(
srs_flv_t
flv
,
char
type
,
int32_t
time
,
char
*
data
,
int
size
)
{
int
ret
=
ERROR_SUCCESS
;
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
if
(
!
context
->
writer
.
is_open
())
{
return
ERROR_SYSTEM_IO_INVALID
;
}
if
(
type
==
SRS_RTMP_TYPE_AUDIO
)
{
return
context
->
enc
.
write_audio
(
time
,
data
,
size
);
}
else
if
(
type
==
SRS_RTMP_TYPE_VIDEO
)
{
return
context
->
enc
.
write_video
(
time
,
data
,
size
);
}
else
{
return
context
->
enc
.
write_metadata
(
data
,
size
);
}
return
ret
;
}
int
srs_flv_size_tag
(
int
data_size
)
{
return
SrsFlvEncoder
::
size_tag
(
data_size
);
}
int64_t
srs_flv_tellg
(
srs_flv_t
flv
)
{
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
return
context
->
reader
.
tellg
();
}
void
srs_flv_lseek
(
srs_flv_t
flv
,
int64_t
offset
)
{
FlvContext
*
context
=
(
FlvContext
*
)
flv
;
context
->
reader
.
lseek
(
offset
);
}
srs_flv_bool
srs_flv_is_eof
(
int
error_code
)
{
return
error_code
==
ERROR_SYSTEM_FILE_EOF
;
}
srs_flv_bool
srs_flv_is_sequence_header
(
char
*
data
,
int32_t
size
)
{
return
SrsFlvCodec
::
video_is_sequence_header
(
data
,
(
int
)
size
);
}
srs_flv_bool
srs_flv_is_keyframe
(
char
*
data
,
int32_t
size
)
{
return
SrsFlvCodec
::
video_is_keyframe
(
data
,
(
int
)
size
);
}
srs_amf0_t
srs_amf0_parse
(
char
*
data
,
int
size
,
int
*
nparsed
)
{
int
ret
=
ERROR_SUCCESS
;
srs_amf0_t
amf0
=
NULL
;
SrsStream
stream
;
if
((
ret
=
stream
.
initialize
(
data
,
size
))
!=
ERROR_SUCCESS
)
{
return
amf0
;
}
SrsAmf0Any
*
any
=
NULL
;
if
((
ret
=
SrsAmf0Any
::
discovery
(
&
stream
,
&
any
))
!=
ERROR_SUCCESS
)
{
return
amf0
;
}
stream
.
skip
(
-
1
*
stream
.
pos
());
if
((
ret
=
any
->
read
(
&
stream
))
!=
ERROR_SUCCESS
)
{
srs_freep
(
any
);
return
amf0
;
}
if
(
nparsed
)
{
*
nparsed
=
stream
.
pos
();
}
amf0
=
(
srs_amf0_t
)
any
;
return
amf0
;
}
srs_amf0_t
srs_amf0_create_number
(
srs_amf0_number
value
)
{
return
SrsAmf0Any
::
number
(
value
);
}
srs_amf0_t
srs_amf0_create_ecma_array
()
{
return
SrsAmf0Any
::
ecma_array
();
}
srs_amf0_t
srs_amf0_create_strict_array
()
{
return
SrsAmf0Any
::
strict_array
();
}
...
...
@@ -1321,397 +1712,6 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value)
obj
->
append
(
any
);
}
/**
* write audio raw frame to SRS.
*/
int
srs_audio_write_raw_frame
(
srs_rtmp_t
rtmp
,
char
sound_format
,
char
sound_rate
,
char
sound_size
,
char
sound_type
,
char
aac_packet_type
,
char
*
frame
,
int
frame_size
,
u_int32_t
timestamp
)
{
Context
*
context
=
(
Context
*
)
rtmp
;
srs_assert
(
context
);
// TODO: FIXME: for aac, must send the sequence header first.
// for audio frame, there is 1 or 2 bytes header:
// 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
// 1bytes, AACPacketType for SoundFormat == 10
int
size
=
frame_size
+
1
;
if
(
aac_packet_type
==
SrsCodecAudioAAC
)
{
size
+=
1
;
}
char
*
data
=
new
char
[
size
];
char
*
p
=
data
;
u_int8_t
audio_header
=
sound_type
&
0x01
;
audio_header
|=
(
sound_size
<<
1
)
&
0x02
;
audio_header
|=
(
sound_rate
<<
2
)
&
0x0c
;
audio_header
|=
(
sound_format
<<
4
)
&
0xf0
;
*
p
++
=
audio_header
;
if
(
aac_packet_type
==
SrsCodecAudioAAC
)
{
*
p
++
=
aac_packet_type
;
}
memcpy
(
p
,
frame
,
frame_size
);
return
srs_rtmp_write_packet
(
context
,
SRS_RTMP_TYPE_AUDIO
,
timestamp
,
data
,
size
);
}
/**
* write h264 packet, with rtmp header.
* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame.
* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU.
* @param h264_raw_data the h.264 raw data, user must free it.
*/
int
__srs_write_h264_packet
(
Context
*
context
,
int8_t
frame_type
,
int8_t
avc_packet_type
,
char
*
h264_raw_data
,
int
h264_raw_size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
// the timestamp in rtmp message header is dts.
u_int32_t
timestamp
=
dts
;
// for h264 in RTMP video payload, there is 5bytes header:
// 1bytes, FrameType | CodecID
// 1bytes, AVCPacketType
// 3bytes, CompositionTime, the cts.
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
int
size
=
h264_raw_size
+
5
;
char
*
data
=
new
char
[
size
];
char
*
p
=
data
;
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
// Frame Type, Type of video frame.
// CodecID, Codec Identifier.
// set the rtmp header
*
p
++
=
(
frame_type
<<
4
)
|
SrsCodecVideoAVC
;
// AVCPacketType
*
p
++
=
avc_packet_type
;
// CompositionTime
// pts = dts + cts, or
// cts = pts - dts.
// where cts is the header in rtmp video packet payload header.
u_int32_t
cts
=
pts
-
dts
;
char
*
pp
=
(
char
*
)
&
cts
;
*
p
++
=
pp
[
2
];
*
p
++
=
pp
[
1
];
*
p
++
=
pp
[
0
];
// h.264 raw data.
memcpy
(
p
,
h264_raw_data
,
h264_raw_size
);
return
srs_rtmp_write_packet
(
context
,
SRS_RTMP_TYPE_VIDEO
,
timestamp
,
data
,
size
);
}
/**
* write the h264 sps/pps in context over RTMP.
*/
int
__srs_write_h264_sps_pps
(
Context
*
context
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
// only send when both sps and pps changed.
if
(
!
context
->
h264_sps_changed
||
!
context
->
h264_pps_changed
)
{
return
ret
;
}
// 5bytes sps/pps header:
// configurationVersion, AVCProfileIndication, profile_compatibility,
// AVCLevelIndication, lengthSizeMinusOne
// 3bytes size of sps:
// numOfSequenceParameterSets, sequenceParameterSetLength(2B)
// Nbytes of sps.
// sequenceParameterSetNALUnit
// 3bytes size of pps:
// numOfPictureParameterSets, pictureParameterSetLength
// Nbytes of pps:
// pictureParameterSetNALUnit
int
nb_packet
=
5
+
3
+
(
int
)
context
->
h264_sps
.
length
()
+
3
+
(
int
)
context
->
h264_pps
.
length
();
char
*
packet
=
new
char
[
nb_packet
];
SrsAutoFree
(
char
,
packet
);
// use stream to generate the h264 packet.
SrsStream
stream
;
if
((
ret
=
stream
.
initialize
(
packet
,
nb_packet
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// decode the SPS:
// @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
if
(
true
)
{
srs_assert
((
int
)
context
->
h264_sps
.
length
()
>=
4
);
char
*
frame
=
(
char
*
)
context
->
h264_sps
.
data
();
// @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205
// Baseline profile profile_idc is 66(0x42).
// Main profile profile_idc is 77(0x4d).
// Extended profile profile_idc is 88(0x58).
u_int8_t
profile_idc
=
frame
[
1
];
//u_int8_t constraint_set = frame[2];
u_int8_t
level_idc
=
frame
[
3
];
// generate the sps/pps header
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// configurationVersion
stream
.
write_1bytes
(
0x01
);
// AVCProfileIndication
stream
.
write_1bytes
(
profile_idc
);
// profile_compatibility
stream
.
write_1bytes
(
0x00
);
// AVCLevelIndication
stream
.
write_1bytes
(
level_idc
);
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size,
// so we always set it to 0x03.
stream
.
write_1bytes
(
0x03
);
}
// sps
if
(
true
)
{
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// numOfSequenceParameterSets, always 1
stream
.
write_1bytes
(
0x01
);
// sequenceParameterSetLength
stream
.
write_2bytes
(
context
->
h264_sps
.
length
());
// sequenceParameterSetNALUnit
stream
.
write_string
(
context
->
h264_sps
);
}
// pps
if
(
true
)
{
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// numOfPictureParameterSets, always 1
stream
.
write_1bytes
(
0x01
);
// pictureParameterSetLength
stream
.
write_2bytes
(
context
->
h264_pps
.
length
());
// pictureParameterSetNALUnit
stream
.
write_string
(
context
->
h264_pps
);
}
// reset sps and pps.
context
->
h264_sps_changed
=
false
;
context
->
h264_pps_changed
=
false
;
context
->
h264_sps_pps_sent
=
true
;
// TODO: FIXME: for more profile.
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144
// send out h264 packet.
int8_t
frame_type
=
SrsCodecVideoAVCFrameKeyFrame
;
int8_t
avc_packet_type
=
SrsCodecVideoAVCTypeSequenceHeader
;
return
__srs_write_h264_packet
(
context
,
frame_type
,
avc_packet_type
,
packet
,
nb_packet
,
dts
,
pts
);
}
/**
* write h264 IPB-frame.
*/
int
__srs_write_h264_ipb_frame
(
Context
*
context
,
char
*
data
,
int
size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
// when sps or pps not sent, ignore the packet.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
if
(
!
context
->
h264_sps_pps_sent
)
{
return
ERROR_H264_DROP_BEFORE_SPS_PPS
;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
u_int8_t
nal_unit_type
=
(
char
)
data
[
0
]
&
0x1f
;
// 4bytes size of nalu:
// NALUnitLength
// Nbytes of nalu.
// NALUnit
int
nb_packet
=
4
+
size
;
char
*
packet
=
new
char
[
nb_packet
];
SrsAutoFree
(
char
,
packet
);
// use stream to generate the h264 packet.
SrsStream
stream
;
if
((
ret
=
stream
.
initialize
(
packet
,
nb_packet
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
// lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size
u_int32_t
NAL_unit_length
=
size
;
// mux the avc NALU in "ISO Base Media File Format"
// from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
// NALUnitLength
stream
.
write_4bytes
(
NAL_unit_length
);
// NALUnit
stream
.
write_bytes
(
data
,
size
);
// send out h264 packet.
int8_t
frame_type
=
SrsCodecVideoAVCFrameInterFrame
;
if
(
nal_unit_type
!=
1
)
{
frame_type
=
SrsCodecVideoAVCFrameKeyFrame
;
}
int8_t
avc_packet_type
=
SrsCodecVideoAVCTypeNALU
;
return
__srs_write_h264_packet
(
context
,
frame_type
,
avc_packet_type
,
packet
,
nb_packet
,
dts
,
pts
);
return
ret
;
}
/**
* write h264 raw frame, maybe sps/pps/IPB-frame.
*/
int
__srs_write_h264_raw_frame
(
Context
*
context
,
char
*
frame
,
int
frame_size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
// ignore invalid frame,
// atleast 1bytes for SPS to decode the type
if
(
frame_size
<
1
)
{
return
ret
;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
u_int8_t
nal_unit_type
=
(
char
)
frame
[
0
]
&
0x1f
;
if
(
nal_unit_type
==
7
)
{
// atleast 1bytes for SPS to decode the type, profile, constrain and level.
if
(
frame_size
<
4
)
{
return
ret
;
}
std
::
string
sps
;
sps
.
append
(
frame
,
frame_size
);
if
(
context
->
h264_sps
==
sps
)
{
return
ERROR_H264_DUPLICATED_SPS
;
}
context
->
h264_sps_changed
=
true
;
context
->
h264_sps
=
sps
;
return
__srs_write_h264_sps_pps
(
context
,
dts
,
pts
);
}
else
if
(
nal_unit_type
==
8
)
{
std
::
string
pps
;
pps
.
append
(
frame
,
frame_size
);
if
(
context
->
h264_pps
==
pps
)
{
return
ERROR_H264_DUPLICATED_PPS
;
}
context
->
h264_pps_changed
=
true
;
context
->
h264_pps
=
pps
;
return
__srs_write_h264_sps_pps
(
context
,
dts
,
pts
);
}
else
{
return
__srs_write_h264_ipb_frame
(
context
,
frame
,
frame_size
,
dts
,
pts
);
}
return
ret
;
}
/**
* write h264 multiple frames, in annexb format.
*/
int
srs_h264_write_raw_frames
(
srs_rtmp_t
rtmp
,
char
*
frames
,
int
frames_size
,
u_int32_t
dts
,
u_int32_t
pts
)
{
int
ret
=
ERROR_SUCCESS
;
srs_assert
(
frames
!=
NULL
);
srs_assert
(
frames_size
>
0
);
srs_assert
(
rtmp
!=
NULL
);
Context
*
context
=
(
Context
*
)
rtmp
;
if
((
ret
=
context
->
h264_raw_stream
.
initialize
(
frames
,
frames_size
))
!=
ERROR_SUCCESS
)
{
return
ret
;
}
// use the last error
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
// @see https://github.com/winlinvip/simple-rtmp-server/issues/204
int
error_code_return
=
ret
;
// send each frame.
while
(
!
context
->
h264_raw_stream
.
empty
())
{
// each frame must prefixed by annexb format.
// about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
int
pnb_start_code
=
0
;
if
(
!
srs_avc_startswith_annexb
(
&
context
->
h264_raw_stream
,
&
pnb_start_code
))
{
return
ERROR_H264_API_NO_PREFIXED
;
}
int
start
=
context
->
h264_raw_stream
.
pos
()
+
pnb_start_code
;
// find the last frame prefixed by annexb format.
context
->
h264_raw_stream
.
skip
(
pnb_start_code
);
while
(
!
context
->
h264_raw_stream
.
empty
())
{
if
(
srs_avc_startswith_annexb
(
&
context
->
h264_raw_stream
,
NULL
))
{
break
;
}
context
->
h264_raw_stream
.
skip
(
1
);
}
int
size
=
context
->
h264_raw_stream
.
pos
()
-
start
;
// send out the frame.
char
*
frame
=
context
->
h264_raw_stream
.
data
()
+
start
;
// it may be return error, but we must process all packets.
if
((
ret
=
__srs_write_h264_raw_frame
(
context
,
frame
,
size
,
dts
,
pts
))
!=
ERROR_SUCCESS
)
{
error_code_return
=
ret
;
// ignore known error, process all packets.
if
(
srs_h264_is_dvbsp_error
(
ret
)
||
srs_h264_is_duplicated_sps_error
(
ret
)
||
srs_h264_is_duplicated_pps_error
(
ret
)
)
{
continue
;
}
return
ret
;
}
}
return
error_code_return
;
}
srs_h264_bool
srs_h264_is_dvbsp_error
(
int
error_code
)
{
return
error_code
==
ERROR_H264_DROP_BEFORE_SPS_PPS
;
}
srs_h264_bool
srs_h264_is_duplicated_sps_error
(
int
error_code
)
{
return
error_code
==
ERROR_H264_DUPLICATED_SPS
;
}
srs_h264_bool
srs_h264_is_duplicated_pps_error
(
int
error_code
)
{
return
error_code
==
ERROR_H264_DUPLICATED_PPS
;
}
int
srs_h264_startswith_annexb
(
char
*
h264_raw_data
,
int
h264_raw_size
,
int
*
pnb_start_code
)
{
SrsStream
stream
;
if
(
stream
.
initialize
(
h264_raw_data
,
h264_raw_size
)
!=
ERROR_SUCCESS
)
{
return
false
;
}
return
srs_avc_startswith_annexb
(
&
stream
,
pnb_start_code
);
}
int64_t
srs_utils_get_time_ms
()
{
srs_update_system_time_ms
();
...
...
trunk/src/libs/srs_librtmp.hpp
查看文件 @
2a9bec3
...
...
@@ -264,164 +264,6 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp,
/*************************************************************
**************************************************************
* flv codec
* @example /trunk/research/librtmp/srs_flv_injecter.c
* @example /trunk/research/librtmp/srs_flv_parser.c
* @example /trunk/research/librtmp/srs_ingest_flv.c
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
**************************************************************
*************************************************************/
typedef
void
*
srs_flv_t
;
typedef
int
srs_flv_bool
;
/* open flv file for both read/write. */
extern
srs_flv_t
srs_flv_open_read
(
const
char
*
file
);
extern
srs_flv_t
srs_flv_open_write
(
const
char
*
file
);
extern
void
srs_flv_close
(
srs_flv_t
flv
);
/**
* read the flv header. 9bytes header.
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
* 3bytes, signature, "FLV",
* 1bytes, version, 0x01,
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
* 4bytes, dataoffset, 0x09, The length of this header in bytes
*
* @return 0, success; otherswise, failed.
* @remark, drop the 4bytes zero previous tag size.
*/
extern
int
srs_flv_read_header
(
srs_flv_t
flv
,
char
header
[
9
]);
/**
* read the flv tag header, 1bytes tag, 3bytes data_size,
* 4bytes time, 3bytes stream id.
* @param ptype, output the type of tag, macros:
* SRS_RTMP_TYPE_AUDIO, FlvTagAudio
* SRS_RTMP_TYPE_VIDEO, FlvTagVideo
* SRS_RTMP_TYPE_SCRIPT, FlvTagScript
* @param pdata_size, output the size of tag data.
* @param ptime, output the time of tag, the dts in ms.
*
* @return 0, success; otherswise, failed.
* @remark, user must ensure the next is a tag, srs never check it.
*/
extern
int
srs_flv_read_tag_header
(
srs_flv_t
flv
,
char
*
ptype
,
int32_t
*
pdata_size
,
u_int32_t
*
ptime
);
/**
* read the tag data. drop the 4bytes previous tag size
* @param data, the data to read, user alloc and free it.
* @param size, the size of data to read, get by srs_flv_read_tag_header().
* @remark, srs will ignore and drop the 4bytes previous tag size.
*/
extern
int
srs_flv_read_tag_data
(
srs_flv_t
flv
,
char
*
data
,
int32_t
size
);
/**
* write the flv header. 9bytes header.
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
* 3bytes, signature, "FLV",
* 1bytes, version, 0x01,
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
* 4bytes, dataoffset, 0x09, The length of this header in bytes
*
* @return 0, success; otherswise, failed.
* @remark, auto write the 4bytes zero previous tag size.
*/
extern
int
srs_flv_write_header
(
srs_flv_t
flv
,
char
header
[
9
]);
/**
* write the flv tag to file.
*
* @return 0, success; otherswise, failed.
* @remark, auto write the 4bytes zero previous tag size.
*/
/* write flv tag to file, auto write the 4bytes previous tag size */
extern
int
srs_flv_write_tag
(
srs_flv_t
flv
,
char
type
,
int32_t
time
,
char
*
data
,
int
size
);
/**
* get the tag size, for flv injecter to adjust offset,
* size = tag_header(11B) + data_size + previous_tag(4B)
* @return the size of tag.
*/
extern
int
srs_flv_size_tag
(
int
data_size
);
/* file stream */
/* file stream tellg to get offset */
extern
int64_t
srs_flv_tellg
(
srs_flv_t
flv
);
/* seek file stream, offset is form the start of file */
extern
void
srs_flv_lseek
(
srs_flv_t
flv
,
int64_t
offset
);
/* error code */
/* whether the error code indicates EOF */
extern
srs_flv_bool
srs_flv_is_eof
(
int
error_code
);
/* media codec */
/**
* whether the video body is sequence header
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_data().
*/
extern
srs_flv_bool
srs_flv_is_sequence_header
(
char
*
data
,
int32_t
size
);
/**
* whether the video body is keyframe
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_data().
*/
extern
srs_flv_bool
srs_flv_is_keyframe
(
char
*
data
,
int32_t
size
);
/*************************************************************
**************************************************************
* amf0 codec
* @example /trunk/research/librtmp/srs_ingest_flv.c
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
**************************************************************
*************************************************************/
/* the output handler. */
typedef
void
*
srs_amf0_t
;
typedef
int
srs_amf0_bool
;
typedef
double
srs_amf0_number
;
/**
* parse amf0 from data.
* @param nparsed, the parsed size, NULL to ignore.
* @return the parsed amf0 object. NULL for error.
*/
extern
srs_amf0_t
srs_amf0_parse
(
char
*
data
,
int
size
,
int
*
nparsed
);
extern
srs_amf0_t
srs_amf0_create_number
(
srs_amf0_number
value
);
extern
srs_amf0_t
srs_amf0_create_ecma_array
();
extern
srs_amf0_t
srs_amf0_create_strict_array
();
extern
srs_amf0_t
srs_amf0_create_object
();
extern
void
srs_amf0_free
(
srs_amf0_t
amf0
);
extern
void
srs_amf0_free_bytes
(
char
*
data
);
/* size and to bytes */
extern
int
srs_amf0_size
(
srs_amf0_t
amf0
);
extern
int
srs_amf0_serialize
(
srs_amf0_t
amf0
,
char
*
data
,
int
size
);
/* type detecter */
extern
srs_amf0_bool
srs_amf0_is_string
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_boolean
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_number
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_null
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_object
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_ecma_array
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_strict_array
(
srs_amf0_t
amf0
);
/* value converter */
extern
const
char
*
srs_amf0_to_string
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_to_boolean
(
srs_amf0_t
amf0
);
extern
srs_amf0_number
srs_amf0_to_number
(
srs_amf0_t
amf0
);
/* value setter */
extern
void
srs_amf0_set_number
(
srs_amf0_t
amf0
,
srs_amf0_number
value
);
/* object value converter */
extern
int
srs_amf0_object_property_count
(
srs_amf0_t
amf0
);
extern
const
char
*
srs_amf0_object_property_name_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_object_property_value_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_object_property
(
srs_amf0_t
amf0
,
const
char
*
name
);
extern
void
srs_amf0_object_property_set
(
srs_amf0_t
amf0
,
const
char
*
name
,
srs_amf0_t
value
);
extern
void
srs_amf0_object_clear
(
srs_amf0_t
amf0
);
/* ecma array value converter */
extern
int
srs_amf0_ecma_array_property_count
(
srs_amf0_t
amf0
);
extern
const
char
*
srs_amf0_ecma_array_property_name_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_ecma_array_property_value_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_ecma_array_property
(
srs_amf0_t
amf0
,
const
char
*
name
);
extern
void
srs_amf0_ecma_array_property_set
(
srs_amf0_t
amf0
,
const
char
*
name
,
srs_amf0_t
value
);
/* strict array value converter */
extern
int
srs_amf0_strict_array_property_count
(
srs_amf0_t
amf0
);
extern
srs_amf0_t
srs_amf0_strict_array_property_at
(
srs_amf0_t
amf0
,
int
index
);
extern
void
srs_amf0_strict_array_append
(
srs_amf0_t
amf0
,
srs_amf0_t
value
);
/*************************************************************
**************************************************************
* audio raw codec
**************************************************************
*************************************************************/
...
...
@@ -585,6 +427,164 @@ extern int srs_h264_startswith_annexb(
/*************************************************************
**************************************************************
* flv codec
* @example /trunk/research/librtmp/srs_flv_injecter.c
* @example /trunk/research/librtmp/srs_flv_parser.c
* @example /trunk/research/librtmp/srs_ingest_flv.c
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
**************************************************************
*************************************************************/
typedef
void
*
srs_flv_t
;
typedef
int
srs_flv_bool
;
/* open flv file for both read/write. */
extern
srs_flv_t
srs_flv_open_read
(
const
char
*
file
);
extern
srs_flv_t
srs_flv_open_write
(
const
char
*
file
);
extern
void
srs_flv_close
(
srs_flv_t
flv
);
/**
* read the flv header. 9bytes header.
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
* 3bytes, signature, "FLV",
* 1bytes, version, 0x01,
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
* 4bytes, dataoffset, 0x09, The length of this header in bytes
*
* @return 0, success; otherswise, failed.
* @remark, drop the 4bytes zero previous tag size.
*/
extern
int
srs_flv_read_header
(
srs_flv_t
flv
,
char
header
[
9
]);
/**
* read the flv tag header, 1bytes tag, 3bytes data_size,
* 4bytes time, 3bytes stream id.
* @param ptype, output the type of tag, macros:
* SRS_RTMP_TYPE_AUDIO, FlvTagAudio
* SRS_RTMP_TYPE_VIDEO, FlvTagVideo
* SRS_RTMP_TYPE_SCRIPT, FlvTagScript
* @param pdata_size, output the size of tag data.
* @param ptime, output the time of tag, the dts in ms.
*
* @return 0, success; otherswise, failed.
* @remark, user must ensure the next is a tag, srs never check it.
*/
extern
int
srs_flv_read_tag_header
(
srs_flv_t
flv
,
char
*
ptype
,
int32_t
*
pdata_size
,
u_int32_t
*
ptime
);
/**
* read the tag data. drop the 4bytes previous tag size
* @param data, the data to read, user alloc and free it.
* @param size, the size of data to read, get by srs_flv_read_tag_header().
* @remark, srs will ignore and drop the 4bytes previous tag size.
*/
extern
int
srs_flv_read_tag_data
(
srs_flv_t
flv
,
char
*
data
,
int32_t
size
);
/**
* write the flv header. 9bytes header.
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
* 3bytes, signature, "FLV",
* 1bytes, version, 0x01,
* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
* 4bytes, dataoffset, 0x09, The length of this header in bytes
*
* @return 0, success; otherswise, failed.
* @remark, auto write the 4bytes zero previous tag size.
*/
extern
int
srs_flv_write_header
(
srs_flv_t
flv
,
char
header
[
9
]);
/**
* write the flv tag to file.
*
* @return 0, success; otherswise, failed.
* @remark, auto write the 4bytes zero previous tag size.
*/
/* write flv tag to file, auto write the 4bytes previous tag size */
extern
int
srs_flv_write_tag
(
srs_flv_t
flv
,
char
type
,
int32_t
time
,
char
*
data
,
int
size
);
/**
* get the tag size, for flv injecter to adjust offset,
* size = tag_header(11B) + data_size + previous_tag(4B)
* @return the size of tag.
*/
extern
int
srs_flv_size_tag
(
int
data_size
);
/* file stream */
/* file stream tellg to get offset */
extern
int64_t
srs_flv_tellg
(
srs_flv_t
flv
);
/* seek file stream, offset is form the start of file */
extern
void
srs_flv_lseek
(
srs_flv_t
flv
,
int64_t
offset
);
/* error code */
/* whether the error code indicates EOF */
extern
srs_flv_bool
srs_flv_is_eof
(
int
error_code
);
/* media codec */
/**
* whether the video body is sequence header
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_data().
*/
extern
srs_flv_bool
srs_flv_is_sequence_header
(
char
*
data
,
int32_t
size
);
/**
* whether the video body is keyframe
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_data().
*/
extern
srs_flv_bool
srs_flv_is_keyframe
(
char
*
data
,
int32_t
size
);
/*************************************************************
**************************************************************
* amf0 codec
* @example /trunk/research/librtmp/srs_ingest_flv.c
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
**************************************************************
*************************************************************/
/* the output handler. */
typedef
void
*
srs_amf0_t
;
typedef
int
srs_amf0_bool
;
typedef
double
srs_amf0_number
;
/**
* parse amf0 from data.
* @param nparsed, the parsed size, NULL to ignore.
* @return the parsed amf0 object. NULL for error.
*/
extern
srs_amf0_t
srs_amf0_parse
(
char
*
data
,
int
size
,
int
*
nparsed
);
extern
srs_amf0_t
srs_amf0_create_number
(
srs_amf0_number
value
);
extern
srs_amf0_t
srs_amf0_create_ecma_array
();
extern
srs_amf0_t
srs_amf0_create_strict_array
();
extern
srs_amf0_t
srs_amf0_create_object
();
extern
void
srs_amf0_free
(
srs_amf0_t
amf0
);
extern
void
srs_amf0_free_bytes
(
char
*
data
);
/* size and to bytes */
extern
int
srs_amf0_size
(
srs_amf0_t
amf0
);
extern
int
srs_amf0_serialize
(
srs_amf0_t
amf0
,
char
*
data
,
int
size
);
/* type detecter */
extern
srs_amf0_bool
srs_amf0_is_string
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_boolean
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_number
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_null
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_object
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_ecma_array
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_is_strict_array
(
srs_amf0_t
amf0
);
/* value converter */
extern
const
char
*
srs_amf0_to_string
(
srs_amf0_t
amf0
);
extern
srs_amf0_bool
srs_amf0_to_boolean
(
srs_amf0_t
amf0
);
extern
srs_amf0_number
srs_amf0_to_number
(
srs_amf0_t
amf0
);
/* value setter */
extern
void
srs_amf0_set_number
(
srs_amf0_t
amf0
,
srs_amf0_number
value
);
/* object value converter */
extern
int
srs_amf0_object_property_count
(
srs_amf0_t
amf0
);
extern
const
char
*
srs_amf0_object_property_name_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_object_property_value_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_object_property
(
srs_amf0_t
amf0
,
const
char
*
name
);
extern
void
srs_amf0_object_property_set
(
srs_amf0_t
amf0
,
const
char
*
name
,
srs_amf0_t
value
);
extern
void
srs_amf0_object_clear
(
srs_amf0_t
amf0
);
/* ecma array value converter */
extern
int
srs_amf0_ecma_array_property_count
(
srs_amf0_t
amf0
);
extern
const
char
*
srs_amf0_ecma_array_property_name_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_ecma_array_property_value_at
(
srs_amf0_t
amf0
,
int
index
);
extern
srs_amf0_t
srs_amf0_ecma_array_property
(
srs_amf0_t
amf0
,
const
char
*
name
);
extern
void
srs_amf0_ecma_array_property_set
(
srs_amf0_t
amf0
,
const
char
*
name
,
srs_amf0_t
value
);
/* strict array value converter */
extern
int
srs_amf0_strict_array_property_count
(
srs_amf0_t
amf0
);
extern
srs_amf0_t
srs_amf0_strict_array_property_at
(
srs_amf0_t
amf0
,
int
index
);
extern
void
srs_amf0_strict_array_append
(
srs_amf0_t
amf0
,
srs_amf0_t
value
);
/*************************************************************
**************************************************************
* utilities
**************************************************************
*************************************************************/
...
...
请
注册
或
登录
后发表评论