正在显示
6 个修改的文件
包含
224 行增加
和
28 行删除
| @@ -228,6 +228,7 @@ Supported operating systems and hardware: | @@ -228,6 +228,7 @@ Supported operating systems and hardware: | ||
| 228 | * 2013-10-17, Created.<br/> | 228 | * 2013-10-17, Created.<br/> |
| 229 | 229 | ||
| 230 | ## History | 230 | ## History |
| 231 | +* v2.0, 2014-11-08, fix [#66](https://github.com/winlinvip/simple-rtmp-server/issues/66), srs-librtmp support write h264 raw packet. 2.0.9. | ||
| 231 | * v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.7. | 232 | * v2.0, 2014-10-25, fix [#185](https://github.com/winlinvip/simple-rtmp-server/issues/185), AMF0 support 0x0B the date type codec. 2.0.7. |
| 232 | * v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.6. | 233 | * v2.0, 2014-10-24, fix [#186](https://github.com/winlinvip/simple-rtmp-server/issues/186), hotfix for bug #186, drop connect args when not object. 2.0.6. |
| 233 | * v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3. | 234 | * v2.0, 2014-10-24, rename wiki/xxx to wiki/v1_CN_xxx. 2.0.3. |
不能预览此文件类型
| @@ -19,6 +19,11 @@ amf3_spec_121207.pdf | @@ -19,6 +19,11 @@ amf3_spec_121207.pdf | ||
| 19 | H.264-AVC-ISO_IEC_14496-10.pdf | 19 | H.264-AVC-ISO_IEC_14496-10.pdf |
| 20 | avc标准,编码部分。 | 20 | avc标准,编码部分。 |
| 21 | 21 | ||
| 22 | +H.264-AVC-ISO_IEC_14496-10-2012.pdf | ||
| 23 | + avc标准,编码部分。 | ||
| 24 | + 上面的标准是2003年的,和下面的15是2010年的对不上。 | ||
| 25 | + http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=11466 | ||
| 26 | + | ||
| 22 | H.264-AVC-ISO_IEC_14496-15.pdf | 27 | H.264-AVC-ISO_IEC_14496-15.pdf |
| 23 | avc标准,封装部分。 | 28 | avc标准,封装部分。 |
| 24 | 29 |
| @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | // current release version | 31 | // current release version |
| 32 | #define VERSION_MAJOR 2 | 32 | #define VERSION_MAJOR 2 |
| 33 | #define VERSION_MINOR 0 | 33 | #define VERSION_MINOR 0 |
| 34 | -#define VERSION_REVISION 8 | 34 | +#define VERSION_REVISION 9 |
| 35 | // server info. | 35 | // server info. |
| 36 | #define RTMP_SIG_SRS_KEY "SRS" | 36 | #define RTMP_SIG_SRS_KEY "SRS" |
| 37 | #define RTMP_SIG_SRS_ROLE "origin/edge server" | 37 | #define RTMP_SIG_SRS_ROLE "origin/edge server" |
| @@ -73,6 +73,9 @@ struct Context | @@ -73,6 +73,9 @@ struct Context | ||
| 73 | // for h264 raw stream, | 73 | // for h264 raw stream, |
| 74 | // see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 | 74 | // see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 |
| 75 | SrsStream raw_stream; | 75 | SrsStream raw_stream; |
| 76 | + // about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | ||
| 77 | + std::string h264_sps; | ||
| 78 | + std::string h264_pps; | ||
| 76 | 79 | ||
| 77 | Context() { | 80 | Context() { |
| 78 | rtmp = NULL; | 81 | rtmp = NULL; |
| @@ -999,14 +1002,16 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) | @@ -999,14 +1002,16 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) | ||
| 999 | 1002 | ||
| 1000 | return any->human_print(pdata, psize); | 1003 | return any->human_print(pdata, psize); |
| 1001 | } | 1004 | } |
| 1002 | - | ||
| 1003 | - /*// 5bits, 7.3.1 NAL unit syntax, | ||
| 1004 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 1005 | - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 1006 | - u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
| 1007 | - | ||
| 1008 | - // the RTMP packet header | ||
| 1009 | - | 1005 | + |
| 1006 | +/** | ||
| 1007 | +* write h264 packet, with rtmp header. | ||
| 1008 | +* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. | ||
| 1009 | +* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. | ||
| 1010 | +*/ | ||
| 1011 | +int __srs_write_h264_packet(Context* context, | ||
| 1012 | + int8_t frame_type, int8_t avc_packet_type, | ||
| 1013 | + char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts | ||
| 1014 | +) { | ||
| 1010 | // the timestamp in rtmp message header is dts. | 1015 | // the timestamp in rtmp message header is dts. |
| 1011 | u_int32_t timestamp = dts; | 1016 | u_int32_t timestamp = dts; |
| 1012 | 1017 | ||
| @@ -1018,24 +1023,16 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) | @@ -1018,24 +1023,16 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) | ||
| 1018 | int size = h264_raw_size + 5; | 1023 | int size = h264_raw_size + 5; |
| 1019 | char* data = new char[size]; | 1024 | char* data = new char[size]; |
| 1020 | memcpy(data + 5, h264_raw_data, h264_raw_size); | 1025 | memcpy(data + 5, h264_raw_data, h264_raw_size); |
| 1026 | + char* p = data; | ||
| 1021 | 1027 | ||
| 1022 | - // Frame Type, Type of video frame. | ||
| 1023 | // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | 1028 | // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 |
| 1024 | - int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; | ||
| 1025 | - if (nal_unit_type != 1) { | ||
| 1026 | - frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 1027 | - } | 1029 | + // Frame Type, Type of video frame. |
| 1028 | // CodecID, Codec Identifier. | 1030 | // CodecID, Codec Identifier. |
| 1029 | - int8_t codec_id = SrsCodecVideoAVC; | ||
| 1030 | // set the rtmp header | 1031 | // set the rtmp header |
| 1031 | - *p++ = (frame_type << 4) | codec_id; | 1032 | + *p++ = (frame_type << 4) | SrsCodecVideoAVC; |
| 1032 | 1033 | ||
| 1033 | // AVCPacketType | 1034 | // AVCPacketType |
| 1034 | - if (nal_unit_type == 7 || nal_unit_type == 8) { | ||
| 1035 | - *p++ = SrsCodecVideoAVCTypeSequenceHeader; | ||
| 1036 | - } else { | ||
| 1037 | - *p++ = SrsCodecVideoAVCTypeNALU; | ||
| 1038 | - } | 1035 | + *p++ = avc_packet_type; |
| 1039 | 1036 | ||
| 1040 | // CompositionTime | 1037 | // CompositionTime |
| 1041 | // pts = dts + cts, or | 1038 | // pts = dts + cts, or |
| @@ -1045,19 +1042,211 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) | @@ -1045,19 +1042,211 @@ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) | ||
| 1045 | char* pp = (char*)&cts; | 1042 | char* pp = (char*)&cts; |
| 1046 | *p++ = pp[2]; | 1043 | *p++ = pp[2]; |
| 1047 | *p++ = pp[1]; | 1044 | *p++ = pp[1]; |
| 1048 | - *p++ = pp[0];*/ | 1045 | + *p++ = pp[0]; |
| 1046 | + | ||
| 1047 | + return srs_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); | ||
| 1048 | +} | ||
| 1049 | 1049 | ||
| 1050 | -int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, u_int32_t dts, u_int32_t pts) | 1050 | +/** |
| 1051 | +* write the h264 sps/pps in context over RTMP. | ||
| 1052 | +*/ | ||
| 1053 | +int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | ||
| 1051 | { | 1054 | { |
| 1052 | int ret = ERROR_SUCCESS; | 1055 | int ret = ERROR_SUCCESS; |
| 1056 | + | ||
| 1057 | + // when pps or sps not ready, ignore. | ||
| 1058 | + if (context->h264_pps.empty() || context->h264_sps.empty()) { | ||
| 1059 | + return ret; | ||
| 1060 | + } | ||
| 1061 | + | ||
| 1062 | + // 5bytes sps/pps header: | ||
| 1063 | + // configurationVersion, AVCProfileIndication, profile_compatibility, | ||
| 1064 | + // AVCLevelIndication, lengthSizeMinusOne | ||
| 1065 | + // 3bytes size of sps: | ||
| 1066 | + // numOfSequenceParameterSets, sequenceParameterSetLength(2B) | ||
| 1067 | + // Nbytes of sps. | ||
| 1068 | + // sequenceParameterSetNALUnit | ||
| 1069 | + // 3bytes size of pps: | ||
| 1070 | + // numOfPictureParameterSets, pictureParameterSetLength | ||
| 1071 | + // Nbytes of pps: | ||
| 1072 | + // pictureParameterSetNALUnit | ||
| 1073 | + int nb_packet = 5 | ||
| 1074 | + + 3 + (int)context->h264_sps.length() | ||
| 1075 | + + 3 + (int)context->h264_pps.length(); | ||
| 1076 | + char* packet = new char[nb_packet]; | ||
| 1077 | + | ||
| 1078 | + // use stream to generate the h264 packet. | ||
| 1079 | + SrsStream stream; | ||
| 1080 | + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
| 1081 | + return ret; | ||
| 1082 | + } | ||
| 1083 | + | ||
| 1084 | + // decode the SPS: | ||
| 1085 | + // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | ||
| 1086 | + if (true) { | ||
| 1087 | + srs_assert((int)context->h264_sps.length() >= 4); | ||
| 1088 | + char* frame = (char*)context->h264_sps.data(); | ||
| 1089 | + | ||
| 1090 | + // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 | ||
| 1091 | + // Baseline profile profile_idc is 66(0x42). | ||
| 1092 | + // Baseline profile profile_idc is 77(0x4d). | ||
| 1093 | + // Extended profile profile_idc is 88(0x58). | ||
| 1094 | + u_int8_t profile_idc = frame[1]; | ||
| 1095 | + //u_int8_t constraint_set = frame[2]; | ||
| 1096 | + u_int8_t level_idc = frame[3]; | ||
| 1097 | + | ||
| 1098 | + // generate the sps/pps header | ||
| 1099 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1100 | + // configurationVersion | ||
| 1101 | + stream.write_1bytes(0x01); | ||
| 1102 | + // AVCProfileIndication | ||
| 1103 | + stream.write_1bytes(profile_idc); | ||
| 1104 | + // profile_compatibility | ||
| 1105 | + stream.write_1bytes(0x00); | ||
| 1106 | + // AVCLevelIndication | ||
| 1107 | + stream.write_1bytes(level_idc); | ||
| 1108 | + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, | ||
| 1109 | + // so we always set it to 0x03. | ||
| 1110 | + stream.write_1bytes(0x03); | ||
| 1111 | + } | ||
| 1112 | + | ||
| 1113 | + // sps | ||
| 1114 | + if (true) { | ||
| 1115 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1116 | + // numOfSequenceParameterSets, always 1 | ||
| 1117 | + stream.write_1bytes(0x01); | ||
| 1118 | + // sequenceParameterSetLength | ||
| 1119 | + stream.write_2bytes(context->h264_sps.length()); | ||
| 1120 | + // sequenceParameterSetNALUnit | ||
| 1121 | + stream.write_string(context->h264_sps); | ||
| 1122 | + } | ||
| 1123 | + | ||
| 1124 | + // pps | ||
| 1125 | + if (true) { | ||
| 1126 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1127 | + // numOfPictureParameterSets, always 1 | ||
| 1128 | + stream.write_1bytes(0x01); | ||
| 1129 | + // pictureParameterSetLength | ||
| 1130 | + stream.write_2bytes(context->h264_pps.length()); | ||
| 1131 | + // pictureParameterSetNALUnit | ||
| 1132 | + stream.write_string(context->h264_pps); | ||
| 1133 | + } | ||
| 1134 | + | ||
| 1135 | + // TODO: FIXME: for more profile. | ||
| 1136 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1137 | + // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 | ||
| 1138 | + | ||
| 1139 | + // send out h264 packet. | ||
| 1140 | + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 1141 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
| 1142 | + return __srs_write_h264_packet( | ||
| 1143 | + context, frame_type, avc_packet_type, | ||
| 1144 | + packet, nb_packet, dts, pts | ||
| 1145 | + ); | ||
| 1146 | +} | ||
| 1147 | + | ||
| 1148 | +/** | ||
| 1149 | +* write h264 IPB-frame. | ||
| 1150 | +*/ | ||
| 1151 | +int __srs_write_h264_ipb_frame(Context* context, | ||
| 1152 | + char* data, int size, u_int32_t dts, u_int32_t pts | ||
| 1153 | +) { | ||
| 1154 | + int ret = ERROR_SUCCESS; | ||
| 1155 | + | ||
| 1156 | + // 5bits, 7.3.1 NAL unit syntax, | ||
| 1157 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 1158 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 1159 | + u_int8_t nal_unit_type = (char)data[0] & 0x1f; | ||
| 1160 | + | ||
| 1161 | + // 4bytes size of nalu: | ||
| 1162 | + // NALUnitLength | ||
| 1163 | + // Nbytes of nalu. | ||
| 1164 | + // NALUnit | ||
| 1165 | + int nb_packet = 4 + size; | ||
| 1166 | + char* packet = new char[nb_packet]; | ||
| 1167 | + | ||
| 1168 | + // use stream to generate the h264 packet. | ||
| 1169 | + SrsStream stream; | ||
| 1170 | + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
| 1171 | + return ret; | ||
| 1172 | + } | ||
| 1173 | + | ||
| 1174 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1175 | + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size | ||
| 1176 | + u_int32_t NAL_unit_length = size; | ||
| 1177 | + | ||
| 1178 | + // mux the avc NALU in "ISO Base Media File Format" | ||
| 1179 | + // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 | ||
| 1180 | + // NALUnitLength | ||
| 1181 | + stream.write_4bytes(NAL_unit_length); | ||
| 1182 | + // NALUnit | ||
| 1183 | + stream.write_bytes(data, size); | ||
| 1184 | + | ||
| 1185 | + // send out h264 packet. | ||
| 1186 | + int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; | ||
| 1187 | + if (nal_unit_type != 1) { | ||
| 1188 | + frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 1189 | + } | ||
| 1190 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; | ||
| 1191 | + return __srs_write_h264_packet( | ||
| 1192 | + context, frame_type, avc_packet_type, | ||
| 1193 | + packet, nb_packet, dts, pts | ||
| 1194 | + ); | ||
| 1195 | + | ||
| 1053 | return ret; | 1196 | return ret; |
| 1054 | } | 1197 | } |
| 1055 | 1198 | ||
| 1056 | -int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_int32_t dts, u_int32_t pts) | ||
| 1057 | -{ | 1199 | +/** |
| 1200 | +* write h264 raw frame, maybe sps/pps/IPB-frame. | ||
| 1201 | +*/ | ||
| 1202 | +int __srs_write_h264_raw_frame(Context* context, | ||
| 1203 | + char* frame, int frame_size, u_int32_t dts, u_int32_t pts | ||
| 1204 | +) { | ||
| 1205 | + int ret = ERROR_SUCCESS; | ||
| 1206 | + | ||
| 1207 | + // ignore invalid frame, | ||
| 1208 | + // atleast 1bytes for SPS to decode the type | ||
| 1209 | + if (frame_size < 1) { | ||
| 1210 | + return ret; | ||
| 1211 | + } | ||
| 1212 | + | ||
| 1213 | + // 5bits, 7.3.1 NAL unit syntax, | ||
| 1214 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 1215 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 1216 | + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
| 1217 | + | ||
| 1218 | + if (nal_unit_type == 7) { | ||
| 1219 | + // atleast 1bytes for SPS to decode the type, profile, constrain and level. | ||
| 1220 | + if (frame_size < 4) { | ||
| 1221 | + return ret; | ||
| 1222 | + } | ||
| 1223 | + | ||
| 1224 | + context->h264_sps = ""; | ||
| 1225 | + context->h264_sps.append(frame, frame_size); | ||
| 1226 | + | ||
| 1227 | + return __srs_write_h264_sps_pps(context, dts, pts); | ||
| 1228 | + } else if (nal_unit_type == 8) { | ||
| 1229 | + context->h264_pps = ""; | ||
| 1230 | + context->h264_pps.append(frame, frame_size); | ||
| 1231 | + | ||
| 1232 | + return __srs_write_h264_sps_pps(context, dts, pts); | ||
| 1233 | + } else { | ||
| 1234 | + return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); | ||
| 1235 | + } | ||
| 1236 | + | ||
| 1237 | + return ret; | ||
| 1238 | +} | ||
| 1239 | + | ||
| 1240 | +/** | ||
| 1241 | +* write h264 multiple frames, in annexb format. | ||
| 1242 | +*/ | ||
| 1243 | +int srs_write_h264_raw_frames(srs_rtmp_t rtmp, | ||
| 1244 | + char* frames, int frames_size, u_int32_t dts, u_int32_t pts | ||
| 1245 | +) { | ||
| 1058 | int ret = ERROR_SUCCESS; | 1246 | int ret = ERROR_SUCCESS; |
| 1059 | 1247 | ||
| 1060 | - srs_assert(frames_size > 1); | 1248 | + srs_assert(frames != NULL); |
| 1249 | + srs_assert(frames_size > 0); | ||
| 1061 | 1250 | ||
| 1062 | srs_assert(rtmp != NULL); | 1251 | srs_assert(rtmp != NULL); |
| 1063 | Context* context = (Context*)rtmp; | 1252 | Context* context = (Context*)rtmp; |
| @@ -1088,7 +1277,7 @@ int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_ | @@ -1088,7 +1277,7 @@ int srs_write_h264_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, u_ | ||
| 1088 | 1277 | ||
| 1089 | // send out the frame. | 1278 | // send out the frame. |
| 1090 | char* frame = context->raw_stream.data() + start; | 1279 | char* frame = context->raw_stream.data() + start; |
| 1091 | - if ((ret = srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { | 1280 | + if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { |
| 1092 | return ret; | 1281 | return ret; |
| 1093 | } | 1282 | } |
| 1094 | } | 1283 | } |
| @@ -344,12 +344,13 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); | @@ -344,12 +344,13 @@ extern char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); | ||
| 344 | * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) | 344 | * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) |
| 345 | * about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. | 345 | * about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. |
| 346 | * @paam frames_size the size of h264 raw data. | 346 | * @paam frames_size the size of h264 raw data. |
| 347 | -* assert frames_size > 1, at least has 1 bytes header. | 347 | +* assert frames_size > 0, at least has 1 bytes header. |
| 348 | * @param dts the dts of h.264 raw data. | 348 | * @param dts the dts of h.264 raw data. |
| 349 | * @param pts the pts of h.264 raw data. | 349 | * @param pts the pts of h.264 raw data. |
| 350 | * | 350 | * |
| 351 | * @remark, user should free the frames. | 351 | * @remark, user should free the frames. |
| 352 | * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. | 352 | * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. |
| 353 | +* @remark, cts = pts - dts | ||
| 353 | * | 354 | * |
| 354 | * @return 0, success; otherswise, failed. | 355 | * @return 0, success; otherswise, failed. |
| 355 | */ | 356 | */ |
-
请 注册 或 登录 后发表评论