正在显示
2 个修改的文件
包含
693 行增加
和
693 行删除
@@ -858,231 +858,622 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* | @@ -858,231 +858,622 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* | ||
858 | return ret; | 858 | return ret; |
859 | } | 859 | } |
860 | 860 | ||
861 | -struct FlvContext | ||
862 | -{ | ||
863 | - SrsFileReader reader; | ||
864 | - SrsFileWriter writer; | ||
865 | - SrsFlvEncoder enc; | ||
866 | - SrsFlvDecoder dec; | ||
867 | -}; | 861 | +/** |
862 | +* write audio raw frame to SRS. | ||
863 | +*/ | ||
864 | +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, | ||
865 | + char sound_format, char sound_rate, char sound_size, char sound_type, | ||
866 | + char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp | ||
867 | +) { | ||
868 | + Context* context = (Context*)rtmp; | ||
869 | + srs_assert(context); | ||
868 | 870 | ||
869 | -srs_flv_t srs_flv_open_read(const char* file) | ||
870 | -{ | ||
871 | - int ret = ERROR_SUCCESS; | ||
872 | - | ||
873 | - FlvContext* flv = new FlvContext(); | ||
874 | - | ||
875 | - if ((ret = flv->reader.open(file)) != ERROR_SUCCESS) { | ||
876 | - srs_freep(flv); | ||
877 | - return NULL; | ||
878 | - } | 871 | + // TODO: FIXME: for aac, must send the sequence header first. |
879 | 872 | ||
880 | - if ((ret = flv->dec.initialize(&flv->reader)) != ERROR_SUCCESS) { | ||
881 | - srs_freep(flv); | ||
882 | - return NULL; | 873 | + // for audio frame, there is 1 or 2 bytes header: |
874 | + // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType | ||
875 | + // 1bytes, AACPacketType for SoundFormat == 10 | ||
876 | + int size = frame_size + 1; | ||
877 | + if (aac_packet_type == SrsCodecAudioAAC) { | ||
878 | + size += 1; | ||
883 | } | 879 | } |
880 | + char* data = new char[size]; | ||
881 | + char* p = data; | ||
884 | 882 | ||
885 | - return flv; | ||
886 | -} | ||
887 | - | ||
888 | -srs_flv_t srs_flv_open_write(const char* file) | ||
889 | -{ | ||
890 | - int ret = ERROR_SUCCESS; | 883 | + u_int8_t audio_header = sound_type & 0x01; |
884 | + audio_header |= (sound_size << 1) & 0x02; | ||
885 | + audio_header |= (sound_rate << 2) & 0x0c; | ||
886 | + audio_header |= (sound_format << 4) & 0xf0; | ||
891 | 887 | ||
892 | - FlvContext* flv = new FlvContext(); | 888 | + *p++ = audio_header; |
893 | 889 | ||
894 | - if ((ret = flv->writer.open(file)) != ERROR_SUCCESS) { | ||
895 | - srs_freep(flv); | ||
896 | - return NULL; | 890 | + if (aac_packet_type == SrsCodecAudioAAC) { |
891 | + *p++ = aac_packet_type; | ||
897 | } | 892 | } |
898 | 893 | ||
899 | - if ((ret = flv->enc.initialize(&flv->writer)) != ERROR_SUCCESS) { | ||
900 | - srs_freep(flv); | ||
901 | - return NULL; | ||
902 | - } | 894 | + memcpy(p, frame, frame_size); |
903 | 895 | ||
904 | - return flv; | ||
905 | -} | ||
906 | - | ||
907 | -void srs_flv_close(srs_flv_t flv) | ||
908 | -{ | ||
909 | - FlvContext* context = (FlvContext*)flv; | ||
910 | - srs_freep(context); | 896 | + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); |
911 | } | 897 | } |
912 | 898 | ||
913 | -int srs_flv_read_header(srs_flv_t flv, char header[9]) | ||
914 | -{ | ||
915 | - int ret = ERROR_SUCCESS; | 899 | +/** |
900 | +* write h264 packet, with rtmp header. | ||
901 | +* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. | ||
902 | +* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. | ||
903 | +* @param h264_raw_data the h.264 raw data, user must free it. | ||
904 | +*/ | ||
905 | +int __srs_write_h264_packet(Context* context, | ||
906 | + int8_t frame_type, int8_t avc_packet_type, | ||
907 | + char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts | ||
908 | +) { | ||
909 | + // the timestamp in rtmp message header is dts. | ||
910 | + u_int32_t timestamp = dts; | ||
916 | 911 | ||
917 | - FlvContext* context = (FlvContext*)flv; | ||
918 | - | ||
919 | - if (!context->reader.is_open()) { | ||
920 | - return ERROR_SYSTEM_IO_INVALID; | ||
921 | - } | 912 | + // for h264 in RTMP video payload, there is 5bytes header: |
913 | + // 1bytes, FrameType | CodecID | ||
914 | + // 1bytes, AVCPacketType | ||
915 | + // 3bytes, CompositionTime, the cts. | ||
916 | + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
917 | + int size = h264_raw_size + 5; | ||
918 | + char* data = new char[size]; | ||
919 | + char* p = data; | ||
922 | 920 | ||
923 | - if ((ret = context->dec.read_header(header)) != ERROR_SUCCESS) { | ||
924 | - return ret; | ||
925 | - } | 921 | + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 |
922 | + // Frame Type, Type of video frame. | ||
923 | + // CodecID, Codec Identifier. | ||
924 | + // set the rtmp header | ||
925 | + *p++ = (frame_type << 4) | SrsCodecVideoAVC; | ||
926 | 926 | ||
927 | - char ts[4]; // tag size | ||
928 | - if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { | ||
929 | - return ret; | ||
930 | - } | 927 | + // AVCPacketType |
928 | + *p++ = avc_packet_type; | ||
929 | + | ||
930 | + // CompositionTime | ||
931 | + // pts = dts + cts, or | ||
932 | + // cts = pts - dts. | ||
933 | + // where cts is the header in rtmp video packet payload header. | ||
934 | + u_int32_t cts = pts - dts; | ||
935 | + char* pp = (char*)&cts; | ||
936 | + *p++ = pp[2]; | ||
937 | + *p++ = pp[1]; | ||
938 | + *p++ = pp[0]; | ||
931 | 939 | ||
932 | - return ret; | 940 | + // h.264 raw data. |
941 | + memcpy(p, h264_raw_data, h264_raw_size); | ||
942 | + | ||
943 | + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); | ||
933 | } | 944 | } |
934 | 945 | ||
935 | -int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime) | 946 | +/** |
947 | +* write the h264 sps/pps in context over RTMP. | ||
948 | +*/ | ||
949 | +int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | ||
936 | { | 950 | { |
937 | int ret = ERROR_SUCCESS; | 951 | int ret = ERROR_SUCCESS; |
938 | 952 | ||
939 | - FlvContext* context = (FlvContext*)flv; | ||
940 | - | ||
941 | - if (!context->reader.is_open()) { | ||
942 | - return ERROR_SYSTEM_IO_INVALID; | 953 | + // only send when both sps and pps changed. |
954 | + if (!context->h264_sps_changed || !context->h264_pps_changed) { | ||
955 | + return ret; | ||
943 | } | 956 | } |
944 | 957 | ||
945 | - if ((ret = context->dec.read_tag_header(ptype, pdata_size, ptime)) != ERROR_SUCCESS) { | 958 | + // 5bytes sps/pps header: |
959 | + // configurationVersion, AVCProfileIndication, profile_compatibility, | ||
960 | + // AVCLevelIndication, lengthSizeMinusOne | ||
961 | + // 3bytes size of sps: | ||
962 | + // numOfSequenceParameterSets, sequenceParameterSetLength(2B) | ||
963 | + // Nbytes of sps. | ||
964 | + // sequenceParameterSetNALUnit | ||
965 | + // 3bytes size of pps: | ||
966 | + // numOfPictureParameterSets, pictureParameterSetLength | ||
967 | + // Nbytes of pps: | ||
968 | + // pictureParameterSetNALUnit | ||
969 | + int nb_packet = 5 | ||
970 | + + 3 + (int)context->h264_sps.length() | ||
971 | + + 3 + (int)context->h264_pps.length(); | ||
972 | + char* packet = new char[nb_packet]; | ||
973 | + SrsAutoFree(char, packet); | ||
974 | + | ||
975 | + // use stream to generate the h264 packet. | ||
976 | + SrsStream stream; | ||
977 | + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
946 | return ret; | 978 | return ret; |
947 | } | 979 | } |
948 | 980 | ||
949 | - return ret; | ||
950 | -} | ||
951 | - | ||
952 | -int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) | ||
953 | -{ | ||
954 | - int ret = ERROR_SUCCESS; | 981 | + // decode the SPS: |
982 | + // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | ||
983 | + if (true) { | ||
984 | + srs_assert((int)context->h264_sps.length() >= 4); | ||
985 | + char* frame = (char*)context->h264_sps.data(); | ||
955 | 986 | ||
956 | - FlvContext* context = (FlvContext*)flv; | ||
957 | - | ||
958 | - if (!context->reader.is_open()) { | ||
959 | - return ERROR_SYSTEM_IO_INVALID; | 987 | + // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 |
988 | + // Baseline profile profile_idc is 66(0x42). | ||
989 | + // Main profile profile_idc is 77(0x4d). | ||
990 | + // Extended profile profile_idc is 88(0x58). | ||
991 | + u_int8_t profile_idc = frame[1]; | ||
992 | + //u_int8_t constraint_set = frame[2]; | ||
993 | + u_int8_t level_idc = frame[3]; | ||
994 | + | ||
995 | + // generate the sps/pps header | ||
996 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
997 | + // configurationVersion | ||
998 | + stream.write_1bytes(0x01); | ||
999 | + // AVCProfileIndication | ||
1000 | + stream.write_1bytes(profile_idc); | ||
1001 | + // profile_compatibility | ||
1002 | + stream.write_1bytes(0x00); | ||
1003 | + // AVCLevelIndication | ||
1004 | + stream.write_1bytes(level_idc); | ||
1005 | + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, | ||
1006 | + // so we always set it to 0x03. | ||
1007 | + stream.write_1bytes(0x03); | ||
960 | } | 1008 | } |
961 | 1009 | ||
962 | - if ((ret = context->dec.read_tag_data(data, size)) != ERROR_SUCCESS) { | ||
963 | - return ret; | 1010 | + // sps |
1011 | + if (true) { | ||
1012 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1013 | + // numOfSequenceParameterSets, always 1 | ||
1014 | + stream.write_1bytes(0x01); | ||
1015 | + // sequenceParameterSetLength | ||
1016 | + stream.write_2bytes(context->h264_sps.length()); | ||
1017 | + // sequenceParameterSetNALUnit | ||
1018 | + stream.write_string(context->h264_sps); | ||
964 | } | 1019 | } |
965 | 1020 | ||
966 | - char ts[4]; // tag size | ||
967 | - if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { | ||
968 | - return ret; | 1021 | + // pps |
1022 | + if (true) { | ||
1023 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1024 | + // numOfPictureParameterSets, always 1 | ||
1025 | + stream.write_1bytes(0x01); | ||
1026 | + // pictureParameterSetLength | ||
1027 | + stream.write_2bytes(context->h264_pps.length()); | ||
1028 | + // pictureParameterSetNALUnit | ||
1029 | + stream.write_string(context->h264_pps); | ||
969 | } | 1030 | } |
970 | 1031 | ||
971 | - return ret; | ||
972 | -} | ||
973 | - | ||
974 | -int srs_flv_write_header(srs_flv_t flv, char header[9]) | ||
975 | -{ | ||
976 | - int ret = ERROR_SUCCESS; | ||
977 | - | ||
978 | - FlvContext* context = (FlvContext*)flv; | ||
979 | - | ||
980 | - if (!context->writer.is_open()) { | ||
981 | - return ERROR_SYSTEM_IO_INVALID; | ||
982 | - } | 1032 | + // reset sps and pps. |
1033 | + context->h264_sps_changed = false; | ||
1034 | + context->h264_pps_changed = false; | ||
1035 | + context->h264_sps_pps_sent = true; | ||
983 | 1036 | ||
984 | - if ((ret = context->enc.write_header(header)) != ERROR_SUCCESS) { | ||
985 | - return ret; | ||
986 | - } | 1037 | + // TODO: FIXME: for more profile. |
1038 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1039 | + // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 | ||
987 | 1040 | ||
988 | - return ret; | 1041 | + // send out h264 packet. |
1042 | + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
1043 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
1044 | + return __srs_write_h264_packet( | ||
1045 | + context, frame_type, avc_packet_type, | ||
1046 | + packet, nb_packet, dts, pts | ||
1047 | + ); | ||
989 | } | 1048 | } |
990 | 1049 | ||
991 | -int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) | ||
992 | -{ | 1050 | +/** |
1051 | +* write h264 IPB-frame. | ||
1052 | +*/ | ||
1053 | +int __srs_write_h264_ipb_frame(Context* context, | ||
1054 | + char* data, int size, u_int32_t dts, u_int32_t pts | ||
1055 | +) { | ||
993 | int ret = ERROR_SUCCESS; | 1056 | int ret = ERROR_SUCCESS; |
994 | 1057 | ||
995 | - FlvContext* context = (FlvContext*)flv; | 1058 | + // when sps or pps not sent, ignore the packet. |
1059 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
1060 | + if (!context->h264_sps_pps_sent) { | ||
1061 | + return ERROR_H264_DROP_BEFORE_SPS_PPS; | ||
1062 | + } | ||
1063 | + | ||
1064 | + // 5bits, 7.3.1 NAL unit syntax, | ||
1065 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
1066 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
1067 | + u_int8_t nal_unit_type = (char)data[0] & 0x1f; | ||
1068 | + | ||
1069 | + // 4bytes size of nalu: | ||
1070 | + // NALUnitLength | ||
1071 | + // Nbytes of nalu. | ||
1072 | + // NALUnit | ||
1073 | + int nb_packet = 4 + size; | ||
1074 | + char* packet = new char[nb_packet]; | ||
1075 | + SrsAutoFree(char, packet); | ||
1076 | + | ||
1077 | + // use stream to generate the h264 packet. | ||
1078 | + SrsStream stream; | ||
1079 | + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
1080 | + return ret; | ||
1081 | + } | ||
996 | 1082 | ||
997 | - if (!context->writer.is_open()) { | ||
998 | - return ERROR_SYSTEM_IO_INVALID; | 1083 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 |
1084 | + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size | ||
1085 | + u_int32_t NAL_unit_length = size; | ||
1086 | + | ||
1087 | + // mux the avc NALU in "ISO Base Media File Format" | ||
1088 | + // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 | ||
1089 | + // NALUnitLength | ||
1090 | + stream.write_4bytes(NAL_unit_length); | ||
1091 | + // NALUnit | ||
1092 | + stream.write_bytes(data, size); | ||
1093 | + | ||
1094 | + // send out h264 packet. | ||
1095 | + int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; | ||
1096 | + if (nal_unit_type != 1) { | ||
1097 | + frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
999 | } | 1098 | } |
1099 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; | ||
1100 | + return __srs_write_h264_packet( | ||
1101 | + context, frame_type, avc_packet_type, | ||
1102 | + packet, nb_packet, dts, pts | ||
1103 | + ); | ||
1000 | 1104 | ||
1001 | - if (type == SRS_RTMP_TYPE_AUDIO) { | ||
1002 | - return context->enc.write_audio(time, data, size); | ||
1003 | - } else if (type == SRS_RTMP_TYPE_VIDEO) { | ||
1004 | - return context->enc.write_video(time, data, size); | 1105 | + return ret; |
1106 | +} | ||
1107 | + | ||
1108 | +/** | ||
1109 | +* write h264 raw frame, maybe sps/pps/IPB-frame. | ||
1110 | +*/ | ||
1111 | +int __srs_write_h264_raw_frame(Context* context, | ||
1112 | + char* frame, int frame_size, u_int32_t dts, u_int32_t pts | ||
1113 | +) { | ||
1114 | + int ret = ERROR_SUCCESS; | ||
1115 | + | ||
1116 | + // ignore invalid frame, | ||
1117 | + // atleast 1bytes for SPS to decode the type | ||
1118 | + if (frame_size < 1) { | ||
1119 | + return ret; | ||
1120 | + } | ||
1121 | + | ||
1122 | + // 5bits, 7.3.1 NAL unit syntax, | ||
1123 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
1124 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
1125 | + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
1126 | + | ||
1127 | + if (nal_unit_type == 7) { | ||
1128 | + // atleast 1bytes for SPS to decode the type, profile, constrain and level. | ||
1129 | + if (frame_size < 4) { | ||
1130 | + return ret; | ||
1131 | + } | ||
1132 | + | ||
1133 | + std::string sps; | ||
1134 | + sps.append(frame, frame_size); | ||
1135 | + | ||
1136 | + if (context->h264_sps == sps) { | ||
1137 | + return ERROR_H264_DUPLICATED_SPS; | ||
1138 | + } | ||
1139 | + context->h264_sps_changed = true; | ||
1140 | + context->h264_sps = sps; | ||
1141 | + | ||
1142 | + return __srs_write_h264_sps_pps(context, dts, pts); | ||
1143 | + } else if (nal_unit_type == 8) { | ||
1144 | + | ||
1145 | + std::string pps; | ||
1146 | + pps.append(frame, frame_size); | ||
1147 | + | ||
1148 | + if (context->h264_pps == pps) { | ||
1149 | + return ERROR_H264_DUPLICATED_PPS; | ||
1150 | + } | ||
1151 | + context->h264_pps_changed = true; | ||
1152 | + context->h264_pps = pps; | ||
1153 | + | ||
1154 | + return __srs_write_h264_sps_pps(context, dts, pts); | ||
1005 | } else { | 1155 | } else { |
1006 | - return context->enc.write_metadata(data, size); | 1156 | + return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); |
1007 | } | 1157 | } |
1008 | - | 1158 | + |
1009 | return ret; | 1159 | return ret; |
1010 | } | 1160 | } |
1011 | 1161 | ||
1012 | -int srs_flv_size_tag(int data_size) | ||
1013 | -{ | ||
1014 | - return SrsFlvEncoder::size_tag(data_size); | 1162 | +/** |
1163 | +* write h264 multiple frames, in annexb format. | ||
1164 | +*/ | ||
1165 | +int srs_h264_write_raw_frames(srs_rtmp_t rtmp, | ||
1166 | + char* frames, int frames_size, u_int32_t dts, u_int32_t pts | ||
1167 | +) { | ||
1168 | + int ret = ERROR_SUCCESS; | ||
1169 | + | ||
1170 | + srs_assert(frames != NULL); | ||
1171 | + srs_assert(frames_size > 0); | ||
1172 | + | ||
1173 | + srs_assert(rtmp != NULL); | ||
1174 | + Context* context = (Context*)rtmp; | ||
1175 | + | ||
1176 | + if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) { | ||
1177 | + return ret; | ||
1178 | + } | ||
1179 | + | ||
1180 | + // use the last error | ||
1181 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
1182 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/204 | ||
1183 | + int error_code_return = ret; | ||
1184 | + | ||
1185 | + // send each frame. | ||
1186 | + while (!context->h264_raw_stream.empty()) { | ||
1187 | + // each frame must prefixed by annexb format. | ||
1188 | + // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. | ||
1189 | + int pnb_start_code = 0; | ||
1190 | + if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) { | ||
1191 | + return ERROR_H264_API_NO_PREFIXED; | ||
1192 | + } | ||
1193 | + int start = context->h264_raw_stream.pos() + pnb_start_code; | ||
1194 | + | ||
1195 | + // find the last frame prefixed by annexb format. | ||
1196 | + context->h264_raw_stream.skip(pnb_start_code); | ||
1197 | + while (!context->h264_raw_stream.empty()) { | ||
1198 | + if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) { | ||
1199 | + break; | ||
1200 | + } | ||
1201 | + context->h264_raw_stream.skip(1); | ||
1202 | + } | ||
1203 | + int size = context->h264_raw_stream.pos() - start; | ||
1204 | + | ||
1205 | + // send out the frame. | ||
1206 | + char* frame = context->h264_raw_stream.data() + start; | ||
1207 | + | ||
1208 | + // it may be return error, but we must process all packets. | ||
1209 | + if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { | ||
1210 | + error_code_return = ret; | ||
1211 | + | ||
1212 | + // ignore known error, process all packets. | ||
1213 | + if (srs_h264_is_dvbsp_error(ret) | ||
1214 | + || srs_h264_is_duplicated_sps_error(ret) | ||
1215 | + || srs_h264_is_duplicated_pps_error(ret) | ||
1216 | + ) { | ||
1217 | + continue; | ||
1218 | + } | ||
1219 | + | ||
1220 | + return ret; | ||
1221 | + } | ||
1222 | + } | ||
1223 | + | ||
1224 | + return error_code_return; | ||
1015 | } | 1225 | } |
1016 | 1226 | ||
1017 | -int64_t srs_flv_tellg(srs_flv_t flv) | 1227 | +srs_h264_bool srs_h264_is_dvbsp_error(int error_code) |
1018 | { | 1228 | { |
1019 | - FlvContext* context = (FlvContext*)flv; | ||
1020 | - return context->reader.tellg(); | 1229 | + return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; |
1021 | } | 1230 | } |
1022 | 1231 | ||
1023 | -void srs_flv_lseek(srs_flv_t flv, int64_t offset) | 1232 | +srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code) |
1024 | { | 1233 | { |
1025 | - FlvContext* context = (FlvContext*)flv; | ||
1026 | - context->reader.lseek(offset); | 1234 | + return error_code == ERROR_H264_DUPLICATED_SPS; |
1027 | } | 1235 | } |
1028 | 1236 | ||
1029 | -srs_flv_bool srs_flv_is_eof(int error_code) | 1237 | +srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code) |
1030 | { | 1238 | { |
1031 | - return error_code == ERROR_SYSTEM_FILE_EOF; | 1239 | + return error_code == ERROR_H264_DUPLICATED_PPS; |
1032 | } | 1240 | } |
1033 | 1241 | ||
1034 | -srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size) | 1242 | +int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) |
1035 | { | 1243 | { |
1036 | - return SrsFlvCodec::video_is_sequence_header(data, (int)size); | 1244 | + SrsStream stream; |
1245 | + if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { | ||
1246 | + return false; | ||
1247 | + } | ||
1248 | + | ||
1249 | + return srs_avc_startswith_annexb(&stream, pnb_start_code); | ||
1037 | } | 1250 | } |
1038 | 1251 | ||
1039 | -srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size) | 1252 | +struct FlvContext |
1040 | { | 1253 | { |
1041 | - return SrsFlvCodec::video_is_keyframe(data, (int)size); | ||
1042 | -} | 1254 | + SrsFileReader reader; |
1255 | + SrsFileWriter writer; | ||
1256 | + SrsFlvEncoder enc; | ||
1257 | + SrsFlvDecoder dec; | ||
1258 | +}; | ||
1043 | 1259 | ||
1044 | -srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) | 1260 | +srs_flv_t srs_flv_open_read(const char* file) |
1045 | { | 1261 | { |
1046 | int ret = ERROR_SUCCESS; | 1262 | int ret = ERROR_SUCCESS; |
1047 | 1263 | ||
1048 | - srs_amf0_t amf0 = NULL; | 1264 | + FlvContext* flv = new FlvContext(); |
1049 | 1265 | ||
1050 | - SrsStream stream; | ||
1051 | - if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) { | ||
1052 | - return amf0; | 1266 | + if ((ret = flv->reader.open(file)) != ERROR_SUCCESS) { |
1267 | + srs_freep(flv); | ||
1268 | + return NULL; | ||
1053 | } | 1269 | } |
1054 | 1270 | ||
1055 | - SrsAmf0Any* any = NULL; | ||
1056 | - if ((ret = SrsAmf0Any::discovery(&stream, &any)) != ERROR_SUCCESS) { | ||
1057 | - return amf0; | 1271 | + if ((ret = flv->dec.initialize(&flv->reader)) != ERROR_SUCCESS) { |
1272 | + srs_freep(flv); | ||
1273 | + return NULL; | ||
1058 | } | 1274 | } |
1059 | 1275 | ||
1060 | - stream.skip(-1 * stream.pos()); | ||
1061 | - if ((ret = any->read(&stream)) != ERROR_SUCCESS) { | ||
1062 | - srs_freep(any); | ||
1063 | - return amf0; | 1276 | + return flv; |
1277 | +} | ||
1278 | + | ||
1279 | +srs_flv_t srs_flv_open_write(const char* file) | ||
1280 | +{ | ||
1281 | + int ret = ERROR_SUCCESS; | ||
1282 | + | ||
1283 | + FlvContext* flv = new FlvContext(); | ||
1284 | + | ||
1285 | + if ((ret = flv->writer.open(file)) != ERROR_SUCCESS) { | ||
1286 | + srs_freep(flv); | ||
1287 | + return NULL; | ||
1064 | } | 1288 | } |
1065 | 1289 | ||
1066 | - if (nparsed) { | ||
1067 | - *nparsed = stream.pos(); | 1290 | + if ((ret = flv->enc.initialize(&flv->writer)) != ERROR_SUCCESS) { |
1291 | + srs_freep(flv); | ||
1292 | + return NULL; | ||
1068 | } | 1293 | } |
1069 | - amf0 = (srs_amf0_t)any; | ||
1070 | 1294 | ||
1071 | - return amf0; | 1295 | + return flv; |
1072 | } | 1296 | } |
1073 | 1297 | ||
1074 | -srs_amf0_t srs_amf0_create_number(srs_amf0_number value) | 1298 | +void srs_flv_close(srs_flv_t flv) |
1075 | { | 1299 | { |
1076 | - return SrsAmf0Any::number(value); | 1300 | + FlvContext* context = (FlvContext*)flv; |
1301 | + srs_freep(context); | ||
1077 | } | 1302 | } |
1078 | 1303 | ||
1079 | -srs_amf0_t srs_amf0_create_ecma_array() | 1304 | +int srs_flv_read_header(srs_flv_t flv, char header[9]) |
1080 | { | 1305 | { |
1081 | - return SrsAmf0Any::ecma_array(); | ||
1082 | -} | 1306 | + int ret = ERROR_SUCCESS; |
1307 | + | ||
1308 | + FlvContext* context = (FlvContext*)flv; | ||
1083 | 1309 | ||
1084 | -srs_amf0_t srs_amf0_create_strict_array() | ||
1085 | -{ | 1310 | + if (!context->reader.is_open()) { |
1311 | + return ERROR_SYSTEM_IO_INVALID; | ||
1312 | + } | ||
1313 | + | ||
1314 | + if ((ret = context->dec.read_header(header)) != ERROR_SUCCESS) { | ||
1315 | + return ret; | ||
1316 | + } | ||
1317 | + | ||
1318 | + char ts[4]; // tag size | ||
1319 | + if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { | ||
1320 | + return ret; | ||
1321 | + } | ||
1322 | + | ||
1323 | + return ret; | ||
1324 | +} | ||
1325 | + | ||
1326 | +int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime) | ||
1327 | +{ | ||
1328 | + int ret = ERROR_SUCCESS; | ||
1329 | + | ||
1330 | + FlvContext* context = (FlvContext*)flv; | ||
1331 | + | ||
1332 | + if (!context->reader.is_open()) { | ||
1333 | + return ERROR_SYSTEM_IO_INVALID; | ||
1334 | + } | ||
1335 | + | ||
1336 | + if ((ret = context->dec.read_tag_header(ptype, pdata_size, ptime)) != ERROR_SUCCESS) { | ||
1337 | + return ret; | ||
1338 | + } | ||
1339 | + | ||
1340 | + return ret; | ||
1341 | +} | ||
1342 | + | ||
1343 | +int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) | ||
1344 | +{ | ||
1345 | + int ret = ERROR_SUCCESS; | ||
1346 | + | ||
1347 | + FlvContext* context = (FlvContext*)flv; | ||
1348 | + | ||
1349 | + if (!context->reader.is_open()) { | ||
1350 | + return ERROR_SYSTEM_IO_INVALID; | ||
1351 | + } | ||
1352 | + | ||
1353 | + if ((ret = context->dec.read_tag_data(data, size)) != ERROR_SUCCESS) { | ||
1354 | + return ret; | ||
1355 | + } | ||
1356 | + | ||
1357 | + char ts[4]; // tag size | ||
1358 | + if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) { | ||
1359 | + return ret; | ||
1360 | + } | ||
1361 | + | ||
1362 | + return ret; | ||
1363 | +} | ||
1364 | + | ||
1365 | +int srs_flv_write_header(srs_flv_t flv, char header[9]) | ||
1366 | +{ | ||
1367 | + int ret = ERROR_SUCCESS; | ||
1368 | + | ||
1369 | + FlvContext* context = (FlvContext*)flv; | ||
1370 | + | ||
1371 | + if (!context->writer.is_open()) { | ||
1372 | + return ERROR_SYSTEM_IO_INVALID; | ||
1373 | + } | ||
1374 | + | ||
1375 | + if ((ret = context->enc.write_header(header)) != ERROR_SUCCESS) { | ||
1376 | + return ret; | ||
1377 | + } | ||
1378 | + | ||
1379 | + return ret; | ||
1380 | +} | ||
1381 | + | ||
1382 | +int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) | ||
1383 | +{ | ||
1384 | + int ret = ERROR_SUCCESS; | ||
1385 | + | ||
1386 | + FlvContext* context = (FlvContext*)flv; | ||
1387 | + | ||
1388 | + if (!context->writer.is_open()) { | ||
1389 | + return ERROR_SYSTEM_IO_INVALID; | ||
1390 | + } | ||
1391 | + | ||
1392 | + if (type == SRS_RTMP_TYPE_AUDIO) { | ||
1393 | + return context->enc.write_audio(time, data, size); | ||
1394 | + } else if (type == SRS_RTMP_TYPE_VIDEO) { | ||
1395 | + return context->enc.write_video(time, data, size); | ||
1396 | + } else { | ||
1397 | + return context->enc.write_metadata(data, size); | ||
1398 | + } | ||
1399 | + | ||
1400 | + return ret; | ||
1401 | +} | ||
1402 | + | ||
1403 | +int srs_flv_size_tag(int data_size) | ||
1404 | +{ | ||
1405 | + return SrsFlvEncoder::size_tag(data_size); | ||
1406 | +} | ||
1407 | + | ||
1408 | +int64_t srs_flv_tellg(srs_flv_t flv) | ||
1409 | +{ | ||
1410 | + FlvContext* context = (FlvContext*)flv; | ||
1411 | + return context->reader.tellg(); | ||
1412 | +} | ||
1413 | + | ||
1414 | +void srs_flv_lseek(srs_flv_t flv, int64_t offset) | ||
1415 | +{ | ||
1416 | + FlvContext* context = (FlvContext*)flv; | ||
1417 | + context->reader.lseek(offset); | ||
1418 | +} | ||
1419 | + | ||
1420 | +srs_flv_bool srs_flv_is_eof(int error_code) | ||
1421 | +{ | ||
1422 | + return error_code == ERROR_SYSTEM_FILE_EOF; | ||
1423 | +} | ||
1424 | + | ||
1425 | +srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size) | ||
1426 | +{ | ||
1427 | + return SrsFlvCodec::video_is_sequence_header(data, (int)size); | ||
1428 | +} | ||
1429 | + | ||
1430 | +srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size) | ||
1431 | +{ | ||
1432 | + return SrsFlvCodec::video_is_keyframe(data, (int)size); | ||
1433 | +} | ||
1434 | + | ||
1435 | +srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) | ||
1436 | +{ | ||
1437 | + int ret = ERROR_SUCCESS; | ||
1438 | + | ||
1439 | + srs_amf0_t amf0 = NULL; | ||
1440 | + | ||
1441 | + SrsStream stream; | ||
1442 | + if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) { | ||
1443 | + return amf0; | ||
1444 | + } | ||
1445 | + | ||
1446 | + SrsAmf0Any* any = NULL; | ||
1447 | + if ((ret = SrsAmf0Any::discovery(&stream, &any)) != ERROR_SUCCESS) { | ||
1448 | + return amf0; | ||
1449 | + } | ||
1450 | + | ||
1451 | + stream.skip(-1 * stream.pos()); | ||
1452 | + if ((ret = any->read(&stream)) != ERROR_SUCCESS) { | ||
1453 | + srs_freep(any); | ||
1454 | + return amf0; | ||
1455 | + } | ||
1456 | + | ||
1457 | + if (nparsed) { | ||
1458 | + *nparsed = stream.pos(); | ||
1459 | + } | ||
1460 | + amf0 = (srs_amf0_t)any; | ||
1461 | + | ||
1462 | + return amf0; | ||
1463 | +} | ||
1464 | + | ||
1465 | +srs_amf0_t srs_amf0_create_number(srs_amf0_number value) | ||
1466 | +{ | ||
1467 | + return SrsAmf0Any::number(value); | ||
1468 | +} | ||
1469 | + | ||
1470 | +srs_amf0_t srs_amf0_create_ecma_array() | ||
1471 | +{ | ||
1472 | + return SrsAmf0Any::ecma_array(); | ||
1473 | +} | ||
1474 | + | ||
1475 | +srs_amf0_t srs_amf0_create_strict_array() | ||
1476 | +{ | ||
1086 | return SrsAmf0Any::strict_array(); | 1477 | return SrsAmf0Any::strict_array(); |
1087 | } | 1478 | } |
1088 | 1479 | ||
@@ -1321,397 +1712,6 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) | @@ -1321,397 +1712,6 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) | ||
1321 | obj->append(any); | 1712 | obj->append(any); |
1322 | } | 1713 | } |
1323 | 1714 | ||
1324 | -/** | ||
1325 | -* write audio raw frame to SRS. | ||
1326 | -*/ | ||
1327 | -int srs_audio_write_raw_frame(srs_rtmp_t rtmp, | ||
1328 | - char sound_format, char sound_rate, char sound_size, char sound_type, | ||
1329 | - char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp | ||
1330 | -) { | ||
1331 | - Context* context = (Context*)rtmp; | ||
1332 | - srs_assert(context); | ||
1333 | - | ||
1334 | - // TODO: FIXME: for aac, must send the sequence header first. | ||
1335 | - | ||
1336 | - // for audio frame, there is 1 or 2 bytes header: | ||
1337 | - // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType | ||
1338 | - // 1bytes, AACPacketType for SoundFormat == 10 | ||
1339 | - int size = frame_size + 1; | ||
1340 | - if (aac_packet_type == SrsCodecAudioAAC) { | ||
1341 | - size += 1; | ||
1342 | - } | ||
1343 | - char* data = new char[size]; | ||
1344 | - char* p = data; | ||
1345 | - | ||
1346 | - u_int8_t audio_header = sound_type & 0x01; | ||
1347 | - audio_header |= (sound_size << 1) & 0x02; | ||
1348 | - audio_header |= (sound_rate << 2) & 0x0c; | ||
1349 | - audio_header |= (sound_format << 4) & 0xf0; | ||
1350 | - | ||
1351 | - *p++ = audio_header; | ||
1352 | - | ||
1353 | - if (aac_packet_type == SrsCodecAudioAAC) { | ||
1354 | - *p++ = aac_packet_type; | ||
1355 | - } | ||
1356 | - | ||
1357 | - memcpy(p, frame, frame_size); | ||
1358 | - | ||
1359 | - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); | ||
1360 | -} | ||
1361 | - | ||
1362 | -/** | ||
1363 | -* write h264 packet, with rtmp header. | ||
1364 | -* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. | ||
1365 | -* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. | ||
1366 | -* @param h264_raw_data the h.264 raw data, user must free it. | ||
1367 | -*/ | ||
1368 | -int __srs_write_h264_packet(Context* context, | ||
1369 | - int8_t frame_type, int8_t avc_packet_type, | ||
1370 | - char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts | ||
1371 | -) { | ||
1372 | - // the timestamp in rtmp message header is dts. | ||
1373 | - u_int32_t timestamp = dts; | ||
1374 | - | ||
1375 | - // for h264 in RTMP video payload, there is 5bytes header: | ||
1376 | - // 1bytes, FrameType | CodecID | ||
1377 | - // 1bytes, AVCPacketType | ||
1378 | - // 3bytes, CompositionTime, the cts. | ||
1379 | - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
1380 | - int size = h264_raw_size + 5; | ||
1381 | - char* data = new char[size]; | ||
1382 | - char* p = data; | ||
1383 | - | ||
1384 | - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
1385 | - // Frame Type, Type of video frame. | ||
1386 | - // CodecID, Codec Identifier. | ||
1387 | - // set the rtmp header | ||
1388 | - *p++ = (frame_type << 4) | SrsCodecVideoAVC; | ||
1389 | - | ||
1390 | - // AVCPacketType | ||
1391 | - *p++ = avc_packet_type; | ||
1392 | - | ||
1393 | - // CompositionTime | ||
1394 | - // pts = dts + cts, or | ||
1395 | - // cts = pts - dts. | ||
1396 | - // where cts is the header in rtmp video packet payload header. | ||
1397 | - u_int32_t cts = pts - dts; | ||
1398 | - char* pp = (char*)&cts; | ||
1399 | - *p++ = pp[2]; | ||
1400 | - *p++ = pp[1]; | ||
1401 | - *p++ = pp[0]; | ||
1402 | - | ||
1403 | - // h.264 raw data. | ||
1404 | - memcpy(p, h264_raw_data, h264_raw_size); | ||
1405 | - | ||
1406 | - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); | ||
1407 | -} | ||
1408 | - | ||
1409 | -/** | ||
1410 | -* write the h264 sps/pps in context over RTMP. | ||
1411 | -*/ | ||
1412 | -int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | ||
1413 | -{ | ||
1414 | - int ret = ERROR_SUCCESS; | ||
1415 | - | ||
1416 | - // only send when both sps and pps changed. | ||
1417 | - if (!context->h264_sps_changed || !context->h264_pps_changed) { | ||
1418 | - return ret; | ||
1419 | - } | ||
1420 | - | ||
1421 | - // 5bytes sps/pps header: | ||
1422 | - // configurationVersion, AVCProfileIndication, profile_compatibility, | ||
1423 | - // AVCLevelIndication, lengthSizeMinusOne | ||
1424 | - // 3bytes size of sps: | ||
1425 | - // numOfSequenceParameterSets, sequenceParameterSetLength(2B) | ||
1426 | - // Nbytes of sps. | ||
1427 | - // sequenceParameterSetNALUnit | ||
1428 | - // 3bytes size of pps: | ||
1429 | - // numOfPictureParameterSets, pictureParameterSetLength | ||
1430 | - // Nbytes of pps: | ||
1431 | - // pictureParameterSetNALUnit | ||
1432 | - int nb_packet = 5 | ||
1433 | - + 3 + (int)context->h264_sps.length() | ||
1434 | - + 3 + (int)context->h264_pps.length(); | ||
1435 | - char* packet = new char[nb_packet]; | ||
1436 | - SrsAutoFree(char, packet); | ||
1437 | - | ||
1438 | - // use stream to generate the h264 packet. | ||
1439 | - SrsStream stream; | ||
1440 | - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
1441 | - return ret; | ||
1442 | - } | ||
1443 | - | ||
1444 | - // decode the SPS: | ||
1445 | - // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | ||
1446 | - if (true) { | ||
1447 | - srs_assert((int)context->h264_sps.length() >= 4); | ||
1448 | - char* frame = (char*)context->h264_sps.data(); | ||
1449 | - | ||
1450 | - // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 | ||
1451 | - // Baseline profile profile_idc is 66(0x42). | ||
1452 | - // Main profile profile_idc is 77(0x4d). | ||
1453 | - // Extended profile profile_idc is 88(0x58). | ||
1454 | - u_int8_t profile_idc = frame[1]; | ||
1455 | - //u_int8_t constraint_set = frame[2]; | ||
1456 | - u_int8_t level_idc = frame[3]; | ||
1457 | - | ||
1458 | - // generate the sps/pps header | ||
1459 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1460 | - // configurationVersion | ||
1461 | - stream.write_1bytes(0x01); | ||
1462 | - // AVCProfileIndication | ||
1463 | - stream.write_1bytes(profile_idc); | ||
1464 | - // profile_compatibility | ||
1465 | - stream.write_1bytes(0x00); | ||
1466 | - // AVCLevelIndication | ||
1467 | - stream.write_1bytes(level_idc); | ||
1468 | - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, | ||
1469 | - // so we always set it to 0x03. | ||
1470 | - stream.write_1bytes(0x03); | ||
1471 | - } | ||
1472 | - | ||
1473 | - // sps | ||
1474 | - if (true) { | ||
1475 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1476 | - // numOfSequenceParameterSets, always 1 | ||
1477 | - stream.write_1bytes(0x01); | ||
1478 | - // sequenceParameterSetLength | ||
1479 | - stream.write_2bytes(context->h264_sps.length()); | ||
1480 | - // sequenceParameterSetNALUnit | ||
1481 | - stream.write_string(context->h264_sps); | ||
1482 | - } | ||
1483 | - | ||
1484 | - // pps | ||
1485 | - if (true) { | ||
1486 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1487 | - // numOfPictureParameterSets, always 1 | ||
1488 | - stream.write_1bytes(0x01); | ||
1489 | - // pictureParameterSetLength | ||
1490 | - stream.write_2bytes(context->h264_pps.length()); | ||
1491 | - // pictureParameterSetNALUnit | ||
1492 | - stream.write_string(context->h264_pps); | ||
1493 | - } | ||
1494 | - | ||
1495 | - // reset sps and pps. | ||
1496 | - context->h264_sps_changed = false; | ||
1497 | - context->h264_pps_changed = false; | ||
1498 | - context->h264_sps_pps_sent = true; | ||
1499 | - | ||
1500 | - // TODO: FIXME: for more profile. | ||
1501 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1502 | - // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 | ||
1503 | - | ||
1504 | - // send out h264 packet. | ||
1505 | - int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
1506 | - int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
1507 | - return __srs_write_h264_packet( | ||
1508 | - context, frame_type, avc_packet_type, | ||
1509 | - packet, nb_packet, dts, pts | ||
1510 | - ); | ||
1511 | -} | ||
1512 | - | ||
1513 | -/** | ||
1514 | -* write h264 IPB-frame. | ||
1515 | -*/ | ||
1516 | -int __srs_write_h264_ipb_frame(Context* context, | ||
1517 | - char* data, int size, u_int32_t dts, u_int32_t pts | ||
1518 | -) { | ||
1519 | - int ret = ERROR_SUCCESS; | ||
1520 | - | ||
1521 | - // when sps or pps not sent, ignore the packet. | ||
1522 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
1523 | - if (!context->h264_sps_pps_sent) { | ||
1524 | - return ERROR_H264_DROP_BEFORE_SPS_PPS; | ||
1525 | - } | ||
1526 | - | ||
1527 | - // 5bits, 7.3.1 NAL unit syntax, | ||
1528 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
1529 | - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
1530 | - u_int8_t nal_unit_type = (char)data[0] & 0x1f; | ||
1531 | - | ||
1532 | - // 4bytes size of nalu: | ||
1533 | - // NALUnitLength | ||
1534 | - // Nbytes of nalu. | ||
1535 | - // NALUnit | ||
1536 | - int nb_packet = 4 + size; | ||
1537 | - char* packet = new char[nb_packet]; | ||
1538 | - SrsAutoFree(char, packet); | ||
1539 | - | ||
1540 | - // use stream to generate the h264 packet. | ||
1541 | - SrsStream stream; | ||
1542 | - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
1543 | - return ret; | ||
1544 | - } | ||
1545 | - | ||
1546 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
1547 | - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size | ||
1548 | - u_int32_t NAL_unit_length = size; | ||
1549 | - | ||
1550 | - // mux the avc NALU in "ISO Base Media File Format" | ||
1551 | - // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 | ||
1552 | - // NALUnitLength | ||
1553 | - stream.write_4bytes(NAL_unit_length); | ||
1554 | - // NALUnit | ||
1555 | - stream.write_bytes(data, size); | ||
1556 | - | ||
1557 | - // send out h264 packet. | ||
1558 | - int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; | ||
1559 | - if (nal_unit_type != 1) { | ||
1560 | - frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
1561 | - } | ||
1562 | - int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; | ||
1563 | - return __srs_write_h264_packet( | ||
1564 | - context, frame_type, avc_packet_type, | ||
1565 | - packet, nb_packet, dts, pts | ||
1566 | - ); | ||
1567 | - | ||
1568 | - return ret; | ||
1569 | -} | ||
1570 | - | ||
1571 | -/** | ||
1572 | -* write h264 raw frame, maybe sps/pps/IPB-frame. | ||
1573 | -*/ | ||
1574 | -int __srs_write_h264_raw_frame(Context* context, | ||
1575 | - char* frame, int frame_size, u_int32_t dts, u_int32_t pts | ||
1576 | -) { | ||
1577 | - int ret = ERROR_SUCCESS; | ||
1578 | - | ||
1579 | - // ignore invalid frame, | ||
1580 | - // atleast 1bytes for SPS to decode the type | ||
1581 | - if (frame_size < 1) { | ||
1582 | - return ret; | ||
1583 | - } | ||
1584 | - | ||
1585 | - // 5bits, 7.3.1 NAL unit syntax, | ||
1586 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
1587 | - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
1588 | - u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
1589 | - | ||
1590 | - if (nal_unit_type == 7) { | ||
1591 | - // atleast 1bytes for SPS to decode the type, profile, constrain and level. | ||
1592 | - if (frame_size < 4) { | ||
1593 | - return ret; | ||
1594 | - } | ||
1595 | - | ||
1596 | - std::string sps; | ||
1597 | - sps.append(frame, frame_size); | ||
1598 | - | ||
1599 | - if (context->h264_sps == sps) { | ||
1600 | - return ERROR_H264_DUPLICATED_SPS; | ||
1601 | - } | ||
1602 | - context->h264_sps_changed = true; | ||
1603 | - context->h264_sps = sps; | ||
1604 | - | ||
1605 | - return __srs_write_h264_sps_pps(context, dts, pts); | ||
1606 | - } else if (nal_unit_type == 8) { | ||
1607 | - | ||
1608 | - std::string pps; | ||
1609 | - pps.append(frame, frame_size); | ||
1610 | - | ||
1611 | - if (context->h264_pps == pps) { | ||
1612 | - return ERROR_H264_DUPLICATED_PPS; | ||
1613 | - } | ||
1614 | - context->h264_pps_changed = true; | ||
1615 | - context->h264_pps = pps; | ||
1616 | - | ||
1617 | - return __srs_write_h264_sps_pps(context, dts, pts); | ||
1618 | - } else { | ||
1619 | - return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); | ||
1620 | - } | ||
1621 | - | ||
1622 | - return ret; | ||
1623 | -} | ||
1624 | - | ||
1625 | -/** | ||
1626 | -* write h264 multiple frames, in annexb format. | ||
1627 | -*/ | ||
1628 | -int srs_h264_write_raw_frames(srs_rtmp_t rtmp, | ||
1629 | - char* frames, int frames_size, u_int32_t dts, u_int32_t pts | ||
1630 | -) { | ||
1631 | - int ret = ERROR_SUCCESS; | ||
1632 | - | ||
1633 | - srs_assert(frames != NULL); | ||
1634 | - srs_assert(frames_size > 0); | ||
1635 | - | ||
1636 | - srs_assert(rtmp != NULL); | ||
1637 | - Context* context = (Context*)rtmp; | ||
1638 | - | ||
1639 | - if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) { | ||
1640 | - return ret; | ||
1641 | - } | ||
1642 | - | ||
1643 | - // use the last error | ||
1644 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
1645 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/204 | ||
1646 | - int error_code_return = ret; | ||
1647 | - | ||
1648 | - // send each frame. | ||
1649 | - while (!context->h264_raw_stream.empty()) { | ||
1650 | - // each frame must prefixed by annexb format. | ||
1651 | - // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. | ||
1652 | - int pnb_start_code = 0; | ||
1653 | - if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) { | ||
1654 | - return ERROR_H264_API_NO_PREFIXED; | ||
1655 | - } | ||
1656 | - int start = context->h264_raw_stream.pos() + pnb_start_code; | ||
1657 | - | ||
1658 | - // find the last frame prefixed by annexb format. | ||
1659 | - context->h264_raw_stream.skip(pnb_start_code); | ||
1660 | - while (!context->h264_raw_stream.empty()) { | ||
1661 | - if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) { | ||
1662 | - break; | ||
1663 | - } | ||
1664 | - context->h264_raw_stream.skip(1); | ||
1665 | - } | ||
1666 | - int size = context->h264_raw_stream.pos() - start; | ||
1667 | - | ||
1668 | - // send out the frame. | ||
1669 | - char* frame = context->h264_raw_stream.data() + start; | ||
1670 | - | ||
1671 | - // it may be return error, but we must process all packets. | ||
1672 | - if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { | ||
1673 | - error_code_return = ret; | ||
1674 | - | ||
1675 | - // ignore known error, process all packets. | ||
1676 | - if (srs_h264_is_dvbsp_error(ret) | ||
1677 | - || srs_h264_is_duplicated_sps_error(ret) | ||
1678 | - || srs_h264_is_duplicated_pps_error(ret) | ||
1679 | - ) { | ||
1680 | - continue; | ||
1681 | - } | ||
1682 | - | ||
1683 | - return ret; | ||
1684 | - } | ||
1685 | - } | ||
1686 | - | ||
1687 | - return error_code_return; | ||
1688 | -} | ||
1689 | - | ||
1690 | -srs_h264_bool srs_h264_is_dvbsp_error(int error_code) | ||
1691 | -{ | ||
1692 | - return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; | ||
1693 | -} | ||
1694 | - | ||
1695 | -srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code) | ||
1696 | -{ | ||
1697 | - return error_code == ERROR_H264_DUPLICATED_SPS; | ||
1698 | -} | ||
1699 | - | ||
1700 | -srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code) | ||
1701 | -{ | ||
1702 | - return error_code == ERROR_H264_DUPLICATED_PPS; | ||
1703 | -} | ||
1704 | - | ||
1705 | -int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) | ||
1706 | -{ | ||
1707 | - SrsStream stream; | ||
1708 | - if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { | ||
1709 | - return false; | ||
1710 | - } | ||
1711 | - | ||
1712 | - return srs_avc_startswith_annexb(&stream, pnb_start_code); | ||
1713 | -} | ||
1714 | - | ||
1715 | int64_t srs_utils_get_time_ms() | 1715 | int64_t srs_utils_get_time_ms() |
1716 | { | 1716 | { |
1717 | srs_update_system_time_ms(); | 1717 | srs_update_system_time_ms(); |
@@ -264,164 +264,6 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, | @@ -264,164 +264,6 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, | ||
264 | 264 | ||
265 | /************************************************************* | 265 | /************************************************************* |
266 | ************************************************************** | 266 | ************************************************************** |
267 | -* flv codec | ||
268 | -* @example /trunk/research/librtmp/srs_flv_injecter.c | ||
269 | -* @example /trunk/research/librtmp/srs_flv_parser.c | ||
270 | -* @example /trunk/research/librtmp/srs_ingest_flv.c | ||
271 | -* @example /trunk/research/librtmp/srs_ingest_rtmp.c | ||
272 | -************************************************************** | ||
273 | -*************************************************************/ | ||
274 | -typedef void* srs_flv_t; | ||
275 | -typedef int srs_flv_bool; | ||
276 | -/* open flv file for both read/write. */ | ||
277 | -extern srs_flv_t srs_flv_open_read(const char* file); | ||
278 | -extern srs_flv_t srs_flv_open_write(const char* file); | ||
279 | -extern void srs_flv_close(srs_flv_t flv); | ||
280 | -/** | ||
281 | -* read the flv header. 9bytes header. | ||
282 | -* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. | ||
283 | -* 3bytes, signature, "FLV", | ||
284 | -* 1bytes, version, 0x01, | ||
285 | -* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. | ||
286 | -* 4bytes, dataoffset, 0x09, The length of this header in bytes | ||
287 | -* | ||
288 | -* @return 0, success; otherswise, failed. | ||
289 | -* @remark, drop the 4bytes zero previous tag size. | ||
290 | -*/ | ||
291 | -extern int srs_flv_read_header(srs_flv_t flv, char header[9]); | ||
292 | -/** | ||
293 | -* read the flv tag header, 1bytes tag, 3bytes data_size, | ||
294 | -* 4bytes time, 3bytes stream id. | ||
295 | -* @param ptype, output the type of tag, macros: | ||
296 | -* SRS_RTMP_TYPE_AUDIO, FlvTagAudio | ||
297 | -* SRS_RTMP_TYPE_VIDEO, FlvTagVideo | ||
298 | -* SRS_RTMP_TYPE_SCRIPT, FlvTagScript | ||
299 | -* @param pdata_size, output the size of tag data. | ||
300 | -* @param ptime, output the time of tag, the dts in ms. | ||
301 | -* | ||
302 | -* @return 0, success; otherswise, failed. | ||
303 | -* @remark, user must ensure the next is a tag, srs never check it. | ||
304 | -*/ | ||
305 | -extern int srs_flv_read_tag_header(srs_flv_t flv, | ||
306 | - char* ptype, int32_t* pdata_size, u_int32_t* ptime | ||
307 | -); | ||
308 | -/** | ||
309 | -* read the tag data. drop the 4bytes previous tag size | ||
310 | -* @param data, the data to read, user alloc and free it. | ||
311 | -* @param size, the size of data to read, get by srs_flv_read_tag_header(). | ||
312 | -* @remark, srs will ignore and drop the 4bytes previous tag size. | ||
313 | -*/ | ||
314 | -extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); | ||
315 | -/** | ||
316 | -* write the flv header. 9bytes header. | ||
317 | -* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. | ||
318 | -* 3bytes, signature, "FLV", | ||
319 | -* 1bytes, version, 0x01, | ||
320 | -* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. | ||
321 | -* 4bytes, dataoffset, 0x09, The length of this header in bytes | ||
322 | -* | ||
323 | -* @return 0, success; otherswise, failed. | ||
324 | -* @remark, auto write the 4bytes zero previous tag size. | ||
325 | -*/ | ||
326 | -extern int srs_flv_write_header(srs_flv_t flv, char header[9]); | ||
327 | -/** | ||
328 | -* write the flv tag to file. | ||
329 | -* | ||
330 | -* @return 0, success; otherswise, failed. | ||
331 | -* @remark, auto write the 4bytes zero previous tag size. | ||
332 | -*/ | ||
333 | -/* write flv tag to file, auto write the 4bytes previous tag size */ | ||
334 | -extern int srs_flv_write_tag(srs_flv_t flv, | ||
335 | - char type, int32_t time, char* data, int size | ||
336 | -); | ||
337 | -/** | ||
338 | -* get the tag size, for flv injecter to adjust offset, | ||
339 | -* size = tag_header(11B) + data_size + previous_tag(4B) | ||
340 | -* @return the size of tag. | ||
341 | -*/ | ||
342 | -extern int srs_flv_size_tag(int data_size); | ||
343 | -/* file stream */ | ||
344 | -/* file stream tellg to get offset */ | ||
345 | -extern int64_t srs_flv_tellg(srs_flv_t flv); | ||
346 | -/* seek file stream, offset is form the start of file */ | ||
347 | -extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); | ||
348 | -/* error code */ | ||
349 | -/* whether the error code indicates EOF */ | ||
350 | -extern srs_flv_bool srs_flv_is_eof(int error_code); | ||
351 | -/* media codec */ | ||
352 | -/** | ||
353 | -* whether the video body is sequence header | ||
354 | -* @param data, the data of tag, read by srs_flv_read_tag_data(). | ||
355 | -* @param size, the size of tag, read by srs_flv_read_tag_data(). | ||
356 | -*/ | ||
357 | -extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size); | ||
358 | -/** | ||
359 | -* whether the video body is keyframe | ||
360 | -* @param data, the data of tag, read by srs_flv_read_tag_data(). | ||
361 | -* @param size, the size of tag, read by srs_flv_read_tag_data(). | ||
362 | -*/ | ||
363 | -extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); | ||
364 | - | ||
365 | -/************************************************************* | ||
366 | -************************************************************** | ||
367 | -* amf0 codec | ||
368 | -* @example /trunk/research/librtmp/srs_ingest_flv.c | ||
369 | -* @example /trunk/research/librtmp/srs_ingest_rtmp.c | ||
370 | -************************************************************** | ||
371 | -*************************************************************/ | ||
372 | -/* the output handler. */ | ||
373 | -typedef void* srs_amf0_t; | ||
374 | -typedef int srs_amf0_bool; | ||
375 | -typedef double srs_amf0_number; | ||
376 | -/** | ||
377 | -* parse amf0 from data. | ||
378 | -* @param nparsed, the parsed size, NULL to ignore. | ||
379 | -* @return the parsed amf0 object. NULL for error. | ||
380 | -*/ | ||
381 | -extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); | ||
382 | -extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); | ||
383 | -extern srs_amf0_t srs_amf0_create_ecma_array(); | ||
384 | -extern srs_amf0_t srs_amf0_create_strict_array(); | ||
385 | -extern srs_amf0_t srs_amf0_create_object(); | ||
386 | -extern void srs_amf0_free(srs_amf0_t amf0); | ||
387 | -extern void srs_amf0_free_bytes(char* data); | ||
388 | -/* size and to bytes */ | ||
389 | -extern int srs_amf0_size(srs_amf0_t amf0); | ||
390 | -extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); | ||
391 | -/* type detecter */ | ||
392 | -extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); | ||
393 | -extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); | ||
394 | -extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); | ||
395 | -extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); | ||
396 | -extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); | ||
397 | -extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); | ||
398 | -extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); | ||
399 | -/* value converter */ | ||
400 | -extern const char* srs_amf0_to_string(srs_amf0_t amf0); | ||
401 | -extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); | ||
402 | -extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); | ||
403 | -/* value setter */ | ||
404 | -extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); | ||
405 | -/* object value converter */ | ||
406 | -extern int srs_amf0_object_property_count(srs_amf0_t amf0); | ||
407 | -extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); | ||
408 | -extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); | ||
409 | -extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); | ||
410 | -extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); | ||
411 | -extern void srs_amf0_object_clear(srs_amf0_t amf0); | ||
412 | -/* ecma array value converter */ | ||
413 | -extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); | ||
414 | -extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); | ||
415 | -extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); | ||
416 | -extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); | ||
417 | -extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); | ||
418 | -/* strict array value converter */ | ||
419 | -extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); | ||
420 | -extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); | ||
421 | -extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); | ||
422 | - | ||
423 | -/************************************************************* | ||
424 | -************************************************************** | ||
425 | * audio raw codec | 267 | * audio raw codec |
426 | ************************************************************** | 268 | ************************************************************** |
427 | *************************************************************/ | 269 | *************************************************************/ |
@@ -585,6 +427,164 @@ extern int srs_h264_startswith_annexb( | @@ -585,6 +427,164 @@ extern int srs_h264_startswith_annexb( | ||
585 | 427 | ||
586 | /************************************************************* | 428 | /************************************************************* |
587 | ************************************************************** | 429 | ************************************************************** |
430 | +* flv codec | ||
431 | +* @example /trunk/research/librtmp/srs_flv_injecter.c | ||
432 | +* @example /trunk/research/librtmp/srs_flv_parser.c | ||
433 | +* @example /trunk/research/librtmp/srs_ingest_flv.c | ||
434 | +* @example /trunk/research/librtmp/srs_ingest_rtmp.c | ||
435 | +************************************************************** | ||
436 | +*************************************************************/ | ||
437 | +typedef void* srs_flv_t; | ||
438 | +typedef int srs_flv_bool; | ||
439 | +/* open flv file for both read/write. */ | ||
440 | +extern srs_flv_t srs_flv_open_read(const char* file); | ||
441 | +extern srs_flv_t srs_flv_open_write(const char* file); | ||
442 | +extern void srs_flv_close(srs_flv_t flv); | ||
443 | +/** | ||
444 | +* read the flv header. 9bytes header. | ||
445 | +* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. | ||
446 | +* 3bytes, signature, "FLV", | ||
447 | +* 1bytes, version, 0x01, | ||
448 | +* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. | ||
449 | +* 4bytes, dataoffset, 0x09, The length of this header in bytes | ||
450 | +* | ||
451 | +* @return 0, success; otherswise, failed. | ||
452 | +* @remark, drop the 4bytes zero previous tag size. | ||
453 | +*/ | ||
454 | +extern int srs_flv_read_header(srs_flv_t flv, char header[9]); | ||
455 | +/** | ||
456 | +* read the flv tag header, 1bytes tag, 3bytes data_size, | ||
457 | +* 4bytes time, 3bytes stream id. | ||
458 | +* @param ptype, output the type of tag, macros: | ||
459 | +* SRS_RTMP_TYPE_AUDIO, FlvTagAudio | ||
460 | +* SRS_RTMP_TYPE_VIDEO, FlvTagVideo | ||
461 | +* SRS_RTMP_TYPE_SCRIPT, FlvTagScript | ||
462 | +* @param pdata_size, output the size of tag data. | ||
463 | +* @param ptime, output the time of tag, the dts in ms. | ||
464 | +* | ||
465 | +* @return 0, success; otherswise, failed. | ||
466 | +* @remark, user must ensure the next is a tag, srs never check it. | ||
467 | +*/ | ||
468 | +extern int srs_flv_read_tag_header(srs_flv_t flv, | ||
469 | + char* ptype, int32_t* pdata_size, u_int32_t* ptime | ||
470 | +); | ||
471 | +/** | ||
472 | +* read the tag data. drop the 4bytes previous tag size | ||
473 | +* @param data, the data to read, user alloc and free it. | ||
474 | +* @param size, the size of data to read, get by srs_flv_read_tag_header(). | ||
475 | +* @remark, srs will ignore and drop the 4bytes previous tag size. | ||
476 | +*/ | ||
477 | +extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); | ||
478 | +/** | ||
479 | +* write the flv header. 9bytes header. | ||
480 | +* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. | ||
481 | +* 3bytes, signature, "FLV", | ||
482 | +* 1bytes, version, 0x01, | ||
483 | +* 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. | ||
484 | +* 4bytes, dataoffset, 0x09, The length of this header in bytes | ||
485 | +* | ||
486 | +* @return 0, success; otherswise, failed. | ||
487 | +* @remark, auto write the 4bytes zero previous tag size. | ||
488 | +*/ | ||
489 | +extern int srs_flv_write_header(srs_flv_t flv, char header[9]); | ||
490 | +/** | ||
491 | +* write the flv tag to file. | ||
492 | +* | ||
493 | +* @return 0, success; otherswise, failed. | ||
494 | +* @remark, auto write the 4bytes zero previous tag size. | ||
495 | +*/ | ||
496 | +/* write flv tag to file, auto write the 4bytes previous tag size */ | ||
497 | +extern int srs_flv_write_tag(srs_flv_t flv, | ||
498 | + char type, int32_t time, char* data, int size | ||
499 | +); | ||
500 | +/** | ||
501 | +* get the tag size, for flv injecter to adjust offset, | ||
502 | +* size = tag_header(11B) + data_size + previous_tag(4B) | ||
503 | +* @return the size of tag. | ||
504 | +*/ | ||
505 | +extern int srs_flv_size_tag(int data_size); | ||
506 | +/* file stream */ | ||
507 | +/* file stream tellg to get offset */ | ||
508 | +extern int64_t srs_flv_tellg(srs_flv_t flv); | ||
509 | +/* seek file stream, offset is form the start of file */ | ||
510 | +extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); | ||
511 | +/* error code */ | ||
512 | +/* whether the error code indicates EOF */ | ||
513 | +extern srs_flv_bool srs_flv_is_eof(int error_code); | ||
514 | +/* media codec */ | ||
515 | +/** | ||
516 | +* whether the video body is sequence header | ||
517 | +* @param data, the data of tag, read by srs_flv_read_tag_data(). | ||
518 | +* @param size, the size of tag, read by srs_flv_read_tag_data(). | ||
519 | +*/ | ||
520 | +extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size); | ||
521 | +/** | ||
522 | +* whether the video body is keyframe | ||
523 | +* @param data, the data of tag, read by srs_flv_read_tag_data(). | ||
524 | +* @param size, the size of tag, read by srs_flv_read_tag_data(). | ||
525 | +*/ | ||
526 | +extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); | ||
527 | + | ||
528 | +/************************************************************* | ||
529 | +************************************************************** | ||
530 | +* amf0 codec | ||
531 | +* @example /trunk/research/librtmp/srs_ingest_flv.c | ||
532 | +* @example /trunk/research/librtmp/srs_ingest_rtmp.c | ||
533 | +************************************************************** | ||
534 | +*************************************************************/ | ||
535 | +/* the output handler. */ | ||
536 | +typedef void* srs_amf0_t; | ||
537 | +typedef int srs_amf0_bool; | ||
538 | +typedef double srs_amf0_number; | ||
539 | +/** | ||
540 | +* parse amf0 from data. | ||
541 | +* @param nparsed, the parsed size, NULL to ignore. | ||
542 | +* @return the parsed amf0 object. NULL for error. | ||
543 | +*/ | ||
544 | +extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); | ||
545 | +extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); | ||
546 | +extern srs_amf0_t srs_amf0_create_ecma_array(); | ||
547 | +extern srs_amf0_t srs_amf0_create_strict_array(); | ||
548 | +extern srs_amf0_t srs_amf0_create_object(); | ||
549 | +extern void srs_amf0_free(srs_amf0_t amf0); | ||
550 | +extern void srs_amf0_free_bytes(char* data); | ||
551 | +/* size and to bytes */ | ||
552 | +extern int srs_amf0_size(srs_amf0_t amf0); | ||
553 | +extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); | ||
554 | +/* type detecter */ | ||
555 | +extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); | ||
556 | +extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); | ||
557 | +extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); | ||
558 | +extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); | ||
559 | +extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); | ||
560 | +extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); | ||
561 | +extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); | ||
562 | +/* value converter */ | ||
563 | +extern const char* srs_amf0_to_string(srs_amf0_t amf0); | ||
564 | +extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); | ||
565 | +extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); | ||
566 | +/* value setter */ | ||
567 | +extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); | ||
568 | +/* object value converter */ | ||
569 | +extern int srs_amf0_object_property_count(srs_amf0_t amf0); | ||
570 | +extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); | ||
571 | +extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); | ||
572 | +extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); | ||
573 | +extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); | ||
574 | +extern void srs_amf0_object_clear(srs_amf0_t amf0); | ||
575 | +/* ecma array value converter */ | ||
576 | +extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); | ||
577 | +extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); | ||
578 | +extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); | ||
579 | +extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); | ||
580 | +extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); | ||
581 | +/* strict array value converter */ | ||
582 | +extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); | ||
583 | +extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); | ||
584 | +extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); | ||
585 | + | ||
586 | +/************************************************************* | ||
587 | +************************************************************** | ||
588 | * utilities | 588 | * utilities |
589 | ************************************************************** | 589 | ************************************************************** |
590 | *************************************************************/ | 590 | *************************************************************/ |
-
请 注册 或 登录 后发表评论