1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

for #250, support h264 video for push mpegts over udp. 2.0.110

This commit is contained in:
winlin 2015-01-31 19:46:55 +08:00
parent 66fccdbbd1
commit 70c8fe13c4
15 changed files with 942 additions and 270 deletions

View file

@ -47,6 +47,7 @@ using namespace std;
#include <srs_kernel_codec.hpp>
#include <srs_kernel_file.hpp>
#include <srs_lib_bandwidth.hpp>
#include <srs_raw_avc.hpp>
// kernel module.
ISrsLog* _srs_log = new ISrsLog();
@ -80,6 +81,9 @@ struct Context
SimpleSocketStream* skt;
int stream_id;
// for h264 raw stream,
// @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521
SrsRawH264Stream avc_raw;
// for h264 raw stream,
// @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521
SrsStream h264_raw_stream;
@ -1021,44 +1025,16 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char*
Context* context = (Context*)rtmp;
SrsSharedPtrMessage* msg = NULL;
if (type == SRS_RTMP_TYPE_AUDIO) {
SrsMessageHeader header;
header.initialize_audio(size, timestamp, context->stream_id);
msg = new SrsSharedPtrMessage();
if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) {
srs_freep(data);
return ret;
}
} else if (type == SRS_RTMP_TYPE_VIDEO) {
SrsMessageHeader header;
header.initialize_video(size, timestamp, context->stream_id);
msg = new SrsSharedPtrMessage();
if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) {
srs_freep(data);
return ret;
}
} else if (type == SRS_RTMP_TYPE_SCRIPT) {
SrsMessageHeader header;
header.initialize_amf0_script(size, context->stream_id);
msg = new SrsSharedPtrMessage();
if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) {
srs_freep(data);
return ret;
}
if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != ERROR_SUCCESS) {
return ret;
}
if (msg) {
// send out encoded msg.
if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) {
return ret;
}
} else {
// directly free data if not sent out.
srs_freep(data);
srs_assert(msg);
// send out encoded msg.
if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) {
return ret;
}
return ret;
@ -1403,52 +1379,37 @@ int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size)
return 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.
* write h264 IPB-frame.
*/
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
int __srs_write_h264_ipb_frame(Context* context,
char* frame, int frame_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;
}
std::string ibp;
int8_t frame_type;
if ((ret = context->avc_raw.mux_ipb_frame(frame, frame_size, dts, pts, ibp, frame_type)) != ERROR_SUCCESS) {
return ret;
}
int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU;
char* flv = NULL;
int nb_flv = 0;
if ((ret = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) {
return ret;
}
// 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);
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv);
}
/**
@ -1463,78 +1424,19 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts)
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) {
// h264 raw to h264 packet.
std::string sh;
if ((ret = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != 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);
// h264 packet to flv packet.
int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame;
int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader;
char* flv = NULL;
int nb_flv = 0;
if ((ret = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) {
return ret;
}
// reset sps and pps.
@ -1542,75 +1444,9 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts)
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;
// the timestamp in rtmp message header is dts.
u_int32_t timestamp = dts;
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv);
}
/**
@ -1620,27 +1456,14 @@ 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) {
// for sps
if (context->avc_raw.is_sps(frame, frame_size)) {
std::string sps;
if ((ret = context->avc_raw.sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) {
return ret;
}
std::string sps;
sps.append(frame, frame_size);
if (context->h264_sps == sps) {
return ERROR_H264_DUPLICATED_SPS;
}
@ -1648,10 +1471,14 @@ int __srs_write_h264_raw_frame(Context* context,
context->h264_sps = sps;
return __srs_write_h264_sps_pps(context, dts, pts);
} else if (nal_unit_type == 8) {
}
// for pps
if (context->avc_raw.is_pps(frame, frame_size)) {
std::string pps;
pps.append(frame, frame_size);
if ((ret = context->avc_raw.pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) {
return ret;
}
if (context->h264_pps == pps) {
return ERROR_H264_DUPLICATED_PPS;
@ -1660,11 +1487,10 @@ int __srs_write_h264_raw_frame(Context* context,
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;
// ibp frame.
return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts);
}
/**
@ -1692,29 +1518,21 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp,
// 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;
char* frame = NULL;
int frame_size = 0;
bool got_sps_pps = false;
if ((ret = context->avc_raw.annexb_demux(&context->h264_raw_stream, &frame, &frame_size)) != ERROR_SUCCESS) {
return ret;
}
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);
// ignore invalid frame,
// atleast 1bytes for SPS to decode the type
if (frame_size <= 0) {
continue;
}
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) {
if ((ret = __srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) {
error_code_return = ret;
// ignore known error, process all packets.