mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
fix #66, srs-librtmp support write h264 raw packet. 2.0.9.
This commit is contained in:
parent
3358570be6
commit
106bef802f
6 changed files with 227 additions and 31 deletions
|
@ -228,6 +228,7 @@ Supported operating systems and hardware:
|
||||||
* 2013-10-17, Created.<br/>
|
* 2013-10-17, Created.<br/>
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
* v2.0, 2014-11-08, fix [#66](https://github.com/winlinvip/simple-rtmp-server/issues/66), srs-librtmp support write h264 raw packet. 2.0.9.
|
||||||
* v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.7.
|
* v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.7.
|
||||||
* v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.6.
|
* v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.6.
|
||||||
* v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3.
|
* v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3.
|
||||||
|
|
BIN
trunk/doc/H.264-AVC-ISO_IEC_14496-10-2012.pdf
Normal file
BIN
trunk/doc/H.264-AVC-ISO_IEC_14496-10-2012.pdf
Normal file
Binary file not shown.
|
@ -19,6 +19,11 @@ amf3_spec_121207.pdf
|
||||||
H.264-AVC-ISO_IEC_14496-10.pdf
|
H.264-AVC-ISO_IEC_14496-10.pdf
|
||||||
avc标准,编码部分。
|
avc标准,编码部分。
|
||||||
|
|
||||||
|
H.264-AVC-ISO_IEC_14496-10-2012.pdf
|
||||||
|
avc标准,编码部分。
|
||||||
|
上面的标准是2003年的,和下面的15是2010年的对不上。
|
||||||
|
http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=11466
|
||||||
|
|
||||||
H.264-AVC-ISO_IEC_14496-15.pdf
|
H.264-AVC-ISO_IEC_14496-15.pdf
|
||||||
avc标准,封装部分。
|
avc标准,封装部分。
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// current release version
|
// current release version
|
||||||
#define VERSION_MAJOR 2
|
#define VERSION_MAJOR 2
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 8
|
#define VERSION_REVISION 9
|
||||||
// server info.
|
// server info.
|
||||||
#define RTMP_SIG_SRS_KEY "SRS"
|
#define RTMP_SIG_SRS_KEY "SRS"
|
||||||
#define RTMP_SIG_SRS_ROLE "origin/edge server"
|
#define RTMP_SIG_SRS_ROLE "origin/edge server"
|
||||||
|
|
|
@ -73,6 +73,9 @@ struct Context
|
||||||
// for h264 raw stream,
|
// for h264 raw stream,
|
||||||
// see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521
|
// see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521
|
||||||
SrsStream raw_stream;
|
SrsStream raw_stream;
|
||||||
|
// about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
|
||||||
|
std::string h264_sps;
|
||||||
|
std::string h264_pps;
|
||||||
|
|
||||||
Context() {
|
Context() {
|
||||||
rtmp = NULL;
|
rtmp = NULL;
|
||||||
|
@ -1000,13 +1003,15 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize)
|
||||||
return any->human_print(pdata, psize);
|
return any->human_print(pdata, psize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*// 5bits, 7.3.1 NAL unit syntax,
|
/**
|
||||||
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
|
* write h264 packet, with rtmp header.
|
||||||
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
|
* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame.
|
||||||
u_int8_t nal_unit_type = (char)frame[0] & 0x1f;
|
* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU.
|
||||||
|
*/
|
||||||
// the RTMP packet header
|
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.
|
// the timestamp in rtmp message header is dts.
|
||||||
u_int32_t timestamp = dts;
|
u_int32_t timestamp = dts;
|
||||||
|
|
||||||
|
@ -1018,24 +1023,16 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize)
|
||||||
int size = h264_raw_size + 5;
|
int size = h264_raw_size + 5;
|
||||||
char* data = new char[size];
|
char* data = new char[size];
|
||||||
memcpy(data + 5, h264_raw_data, h264_raw_size);
|
memcpy(data + 5, h264_raw_data, h264_raw_size);
|
||||||
|
char* p = data;
|
||||||
|
|
||||||
// Frame Type, Type of video frame.
|
|
||||||
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
||||||
int8_t frame_type = SrsCodecVideoAVCFrameInterFrame;
|
// Frame Type, Type of video frame.
|
||||||
if (nal_unit_type != 1) {
|
|
||||||
frame_type = SrsCodecVideoAVCFrameKeyFrame;
|
|
||||||
}
|
|
||||||
// CodecID, Codec Identifier.
|
// CodecID, Codec Identifier.
|
||||||
int8_t codec_id = SrsCodecVideoAVC;
|
|
||||||
// set the rtmp header
|
// set the rtmp header
|
||||||
*p++ = (frame_type << 4) | codec_id;
|
*p++ = (frame_type << 4) | SrsCodecVideoAVC;
|
||||||
|
|
||||||
// AVCPacketType
|
// AVCPacketType
|
||||||
if (nal_unit_type == 7 || nal_unit_type == 8) {
|
*p++ = avc_packet_type;
|
||||||
*p++ = SrsCodecVideoAVCTypeSequenceHeader;
|
|
||||||
} else {
|
|
||||||
*p++ = SrsCodecVideoAVCTypeNALU;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompositionTime
|
// CompositionTime
|
||||||
// pts = dts + cts, or
|
// pts = dts + cts, or
|
||||||
|
@ -1045,19 +1042,211 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize)
|
||||||
char* pp = (char*)&cts;
|
char* pp = (char*)&cts;
|
||||||
*p++ = pp[2];
|
*p++ = pp[2];
|
||||||
*p++ = pp[1];
|
*p++ = pp[1];
|
||||||
*p++ = pp[0];*/
|
*p++ = pp[0];
|
||||||
|
|
||||||
int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, u_int32_t dts, u_int32_t pts)
|
return srs_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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// when pps or sps not ready, ignore.
|
||||||
|
if (context->h264_pps.empty() || context->h264_sps.empty()) {
|
||||||
|
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];
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
// Baseline 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
// 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_int32_t dts, u_int32_t pts)
|
/**
|
||||||
{
|
* 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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
srs_assert(frames_size > 1);
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->h264_sps = "";
|
||||||
|
context->h264_sps.append(frame, frame_size);
|
||||||
|
|
||||||
|
return __srs_write_h264_sps_pps(context, dts, pts);
|
||||||
|
} else if (nal_unit_type == 8) {
|
||||||
|
context->h264_pps = "";
|
||||||
|
context->h264_pps.append(frame, frame_size);
|
||||||
|
|
||||||
|
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_write_h264_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);
|
srs_assert(rtmp != NULL);
|
||||||
Context* context = (Context*)rtmp;
|
Context* context = (Context*)rtmp;
|
||||||
|
@ -1088,7 +1277,7 @@ int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_
|
||||||
|
|
||||||
// send out the frame.
|
// send out the frame.
|
||||||
char* frame = context->raw_stream.data() + start;
|
char* frame = context->raw_stream.data() + start;
|
||||||
if ((ret = srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) {
|
if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,12 +344,13 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize);
|
||||||
* for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40)
|
* for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40)
|
||||||
* about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
|
* about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
|
||||||
* @paam frames_size the size of h264 raw data.
|
* @paam frames_size the size of h264 raw data.
|
||||||
* assert frames_size > 1, at least has 1 bytes header.
|
* assert frames_size > 0, at least has 1 bytes header.
|
||||||
* @param dts the dts of h.264 raw data.
|
* @param dts the dts of h.264 raw data.
|
||||||
* @param pts the pts of h.264 raw data.
|
* @param pts the pts of h.264 raw data.
|
||||||
*
|
*
|
||||||
* @remark, user should free the frames.
|
* @remark, user should free the frames.
|
||||||
* @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms.
|
* @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms.
|
||||||
|
* @remark, cts = pts - dts
|
||||||
*
|
*
|
||||||
* @return 0, success; otherswise, failed.
|
* @return 0, success; otherswise, failed.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue