From 2a9bec3d89d38dd1af9f420301526e47075fe559 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 21 Nov 2014 14:26:47 +0800 Subject: [PATCH] adjust the api, move audio and h264 after rtmp --- trunk/src/libs/srs_librtmp.cpp | 782 ++++++++++++++++----------------- trunk/src/libs/srs_librtmp.hpp | 316 ++++++------- 2 files changed, 549 insertions(+), 549 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 219dcebf2..591faf819 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -858,6 +858,397 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* return ret; } +/** +* 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); +} + struct FlvContext { SrsFileReader reader; @@ -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(); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index d0638eb12..8bed20596 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -262,164 +262,6 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size ); -/************************************************************* -************************************************************** -* 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 @@ -583,6 +425,164 @@ extern int srs_h264_startswith_annexb( int* pnb_start_code ); +/************************************************************* +************************************************************** +* 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