正在显示
7 个修改的文件
包含
829 行增加
和
800 行删除
| @@ -50,6 +50,7 @@ using namespace std; | @@ -50,6 +50,7 @@ using namespace std; | ||
| 50 | #include <srs_kernel_avc.hpp> | 50 | #include <srs_kernel_avc.hpp> |
| 51 | #include <srs_kernel_file.hpp> | 51 | #include <srs_kernel_file.hpp> |
| 52 | #include <srs_rtmp_buffer.hpp> | 52 | #include <srs_rtmp_buffer.hpp> |
| 53 | +#include <srs_kernel_ts.hpp> | ||
| 53 | 54 | ||
| 54 | // drop the segment when duration of ts too small. | 55 | // drop the segment when duration of ts too small. |
| 55 | #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 | 56 | #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 |
| @@ -33,6 +33,9 @@ using namespace std; | @@ -33,6 +33,9 @@ using namespace std; | ||
| 33 | #include <srs_kernel_log.hpp> | 33 | #include <srs_kernel_log.hpp> |
| 34 | #include <srs_app_config.hpp> | 34 | #include <srs_app_config.hpp> |
| 35 | 35 | ||
| 36 | +// Transport Stream packets are 188 bytes in length. | ||
| 37 | +#define TS_PACKET_SIZE 188 | ||
| 38 | + | ||
| 36 | #ifdef SRS_AUTO_STREAM_CASTER | 39 | #ifdef SRS_AUTO_STREAM_CASTER |
| 37 | 40 | ||
| 38 | SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) | 41 | SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) |
| @@ -51,10 +54,28 @@ int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | @@ -51,10 +54,28 @@ int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | ||
| 51 | std::string peer_ip = inet_ntoa(from->sin_addr); | 54 | std::string peer_ip = inet_ntoa(from->sin_addr); |
| 52 | int peer_port = ntohs(from->sin_port); | 55 | int peer_port = ntohs(from->sin_port); |
| 53 | 56 | ||
| 57 | + // drop ts packet when size not modulus by 188 | ||
| 58 | + if (nb_buf < TS_PACKET_SIZE || (nb_buf % TS_PACKET_SIZE) != 0) { | ||
| 59 | + srs_warn("udp: drop %s:%d packet %d bytes", peer_ip.c_str(), peer_port, nb_buf); | ||
| 60 | + return ret; | ||
| 61 | + } | ||
| 54 | srs_info("udp: got %s:%d packet %d bytes", peer_ip.c_str(), peer_port, nb_buf); | 62 | srs_info("udp: got %s:%d packet %d bytes", peer_ip.c_str(), peer_port, nb_buf); |
| 55 | 63 | ||
| 56 | - // TODO: FIXME: implements it. | 64 | + // process each ts packet |
| 65 | + for (int i = 0; i < nb_buf; i += TS_PACKET_SIZE) { | ||
| 66 | + char* ts_packet = buf + i; | ||
| 67 | + if ((ret = on_ts_packet(ts_packet)) != ERROR_SUCCESS) { | ||
| 68 | + srs_warn("mpegts: ignore ts packet error. ret=%d", ret); | ||
| 69 | + continue; | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + return ret; | ||
| 74 | +} | ||
| 57 | 75 | ||
| 76 | +int SrsMpegtsOverUdp::on_ts_packet(char* ts_packet) | ||
| 77 | +{ | ||
| 78 | + int ret = ERROR_SUCCESS; | ||
| 58 | return ret; | 79 | return ret; |
| 59 | } | 80 | } |
| 60 | 81 |
| @@ -58,6 +58,11 @@ public: | @@ -58,6 +58,11 @@ public: | ||
| 58 | * @remark user should never use the buf, for it's a shared memory bytes. | 58 | * @remark user should never use the buf, for it's a shared memory bytes. |
| 59 | */ | 59 | */ |
| 60 | virtual int on_udp_packet(sockaddr_in* from, char* buf, int nb_buf); | 60 | virtual int on_udp_packet(sockaddr_in* from, char* buf, int nb_buf); |
| 61 | +private: | ||
| 62 | + /** | ||
| 63 | + * when got a ts packet, in size TS_PACKET_SIZE. | ||
| 64 | + */ | ||
| 65 | + virtual int on_ts_packet(char* ts_packet); | ||
| 61 | }; | 66 | }; |
| 62 | 67 | ||
| 63 | #endif | 68 | #endif |
| @@ -32,702 +32,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -32,702 +32,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 32 | 32 | ||
| 33 | using namespace std; | 33 | using namespace std; |
| 34 | 34 | ||
| 35 | -// in ms, for HLS aac sync time. | ||
| 36 | -#define SRS_CONF_DEFAULT_AAC_SYNC 100 | ||
| 37 | - | ||
| 38 | -// @see: ngx_rtmp_hls_audio | ||
| 39 | -/* We assume here AAC frame size is 1024 | ||
| 40 | - * Need to handle AAC frames with frame size of 960 */ | ||
| 41 | -#define _SRS_AAC_SAMPLE_SIZE 1024 | ||
| 42 | - | ||
| 43 | -// the mpegts header specifed the video/audio pid. | ||
| 44 | -#define TS_VIDEO_PID 256 | ||
| 45 | -#define TS_AUDIO_PID 257 | ||
| 46 | - | ||
| 47 | -// ts aac stream id. | ||
| 48 | -#define TS_AUDIO_AAC 0xc0 | ||
| 49 | -// ts avc stream id. | ||
| 50 | -#define TS_VIDEO_AVC 0xe0 | ||
| 51 | - | ||
| 52 | -/** | ||
| 53 | -* the public data, event HLS disable, others can use it. | ||
| 54 | -*/ | ||
| 55 | -// 0 = 5.5 kHz = 5512 Hz | ||
| 56 | -// 1 = 11 kHz = 11025 Hz | ||
| 57 | -// 2 = 22 kHz = 22050 Hz | ||
| 58 | -// 3 = 44 kHz = 44100 Hz | ||
| 59 | -int flv_sample_rates[] = {5512, 11025, 22050, 44100}; | ||
| 60 | - | ||
| 61 | -// the sample rates in the codec, | ||
| 62 | -// in the sequence header. | ||
| 63 | -int aac_sample_rates[] = | ||
| 64 | -{ | ||
| 65 | - 96000, 88200, 64000, 48000, | ||
| 66 | - 44100, 32000, 24000, 22050, | ||
| 67 | - 16000, 12000, 11025, 8000, | ||
| 68 | - 7350, 0, 0, 0 | ||
| 69 | -}; | ||
| 70 | - | ||
| 71 | -// @see: NGX_RTMP_HLS_DELAY, | ||
| 72 | -// 63000: 700ms, ts_tbn=90000 | ||
| 73 | -#define SRS_AUTO_HLS_DELAY 63000 | ||
| 74 | - | ||
| 75 | -// @see: ngx_rtmp_mpegts_header | ||
| 76 | -u_int8_t mpegts_header[] = { | ||
| 77 | - /* TS */ | ||
| 78 | - 0x47, 0x40, 0x00, 0x10, 0x00, | ||
| 79 | - /* PSI */ | ||
| 80 | - 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
| 81 | - /* PAT */ | ||
| 82 | - 0x00, 0x01, 0xf0, 0x01, | ||
| 83 | - /* CRC */ | ||
| 84 | - 0x2e, 0x70, 0x19, 0x05, | ||
| 85 | - /* stuffing 167 bytes */ | ||
| 86 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 87 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 88 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 89 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 90 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 91 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 92 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 93 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 94 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 95 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 96 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 97 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 98 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 99 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 100 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 101 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 102 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 103 | - | ||
| 104 | - /* TS */ | ||
| 105 | - 0x47, 0x50, 0x01, 0x10, 0x00, | ||
| 106 | - /* PSI */ | ||
| 107 | - 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
| 108 | - /* PMT */ | ||
| 109 | - 0xe1, 0x00, | ||
| 110 | - 0xf0, 0x00, | ||
| 111 | - // must generate header with/without video, @see: | ||
| 112 | - // https://github.com/winlinvip/simple-rtmp-server/issues/40 | ||
| 113 | - 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ | ||
| 114 | - 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ | ||
| 115 | - /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ | ||
| 116 | - /* CRC */ | ||
| 117 | - 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ | ||
| 118 | - /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ | ||
| 119 | - /* stuffing 157 bytes */ | ||
| 120 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 121 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 122 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 123 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 124 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 125 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 126 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 127 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 128 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 129 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 130 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 131 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 132 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 133 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 134 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 135 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | ||
| 136 | -}; | ||
| 137 | - | ||
| 138 | -// @see: ngx_rtmp_mpegts.c | ||
| 139 | -// TODO: support full mpegts feature in future. | ||
| 140 | -class SrsMpegtsWriter | ||
| 141 | -{ | ||
| 142 | -public: | ||
| 143 | - static int write_header(SrsFileWriter* writer) | ||
| 144 | - { | ||
| 145 | - int ret = ERROR_SUCCESS; | ||
| 146 | - | ||
| 147 | - if ((ret = writer->write(mpegts_header, sizeof(mpegts_header), NULL)) != ERROR_SUCCESS) { | ||
| 148 | - ret = ERROR_HLS_WRITE_FAILED; | ||
| 149 | - srs_error("write ts file header failed. ret=%d", ret); | ||
| 150 | - return ret; | ||
| 151 | - } | ||
| 152 | - | ||
| 153 | - return ret; | ||
| 154 | - } | ||
| 155 | - static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer) | ||
| 156 | - { | ||
| 157 | - int ret = ERROR_SUCCESS; | ||
| 158 | - | ||
| 159 | - if (!buffer->bytes() || buffer->length() <= 0) { | ||
| 160 | - return ret; | ||
| 161 | - } | ||
| 162 | - | ||
| 163 | - char* last = buffer->bytes() + buffer->length(); | ||
| 164 | - char* pos = buffer->bytes(); | ||
| 165 | - | ||
| 166 | - bool first = true; | ||
| 167 | - while (pos < last) { | ||
| 168 | - static char packet[188]; | ||
| 169 | - char* p = packet; | ||
| 170 | - | ||
| 171 | - frame->cc++; | ||
| 172 | - | ||
| 173 | - // sync_byte; //8bits | ||
| 174 | - *p++ = 0x47; | ||
| 175 | - // pid; //13bits | ||
| 176 | - *p++ = (frame->pid >> 8) & 0x1f; | ||
| 177 | - // payload_unit_start_indicator; //1bit | ||
| 178 | - if (first) { | ||
| 179 | - p[-1] |= 0x40; | ||
| 180 | - } | ||
| 181 | - *p++ = frame->pid; | ||
| 182 | - | ||
| 183 | - // transport_scrambling_control; //2bits | ||
| 184 | - // adaption_field_control; //2bits, 0x01: PayloadOnly | ||
| 185 | - // continuity_counter; //4bits | ||
| 186 | - *p++ = 0x10 | (frame->cc & 0x0f); | ||
| 187 | - | ||
| 188 | - if (first) { | ||
| 189 | - first = false; | ||
| 190 | - if (frame->key) { | ||
| 191 | - p[-1] |= 0x20; // Both Adaption and Payload | ||
| 192 | - *p++ = 7; // size | ||
| 193 | - *p++ = 0x50; // random access + PCR | ||
| 194 | - p = write_pcr(p, frame->dts - SRS_AUTO_HLS_DELAY); | ||
| 195 | - } | ||
| 196 | - | ||
| 197 | - // PES header | ||
| 198 | - // packet_start_code_prefix; //24bits, '00 00 01' | ||
| 199 | - *p++ = 0x00; | ||
| 200 | - *p++ = 0x00; | ||
| 201 | - *p++ = 0x01; | ||
| 202 | - //8bits | ||
| 203 | - *p++ = frame->sid; | ||
| 204 | - | ||
| 205 | - // pts(33bits) need 5bytes. | ||
| 206 | - u_int8_t header_size = 5; | ||
| 207 | - u_int8_t flags = 0x80; // pts | ||
| 208 | - | ||
| 209 | - // dts(33bits) need 5bytes also | ||
| 210 | - if (frame->dts != frame->pts) { | ||
| 211 | - header_size += 5; | ||
| 212 | - flags |= 0x40; // dts | ||
| 213 | - } | ||
| 214 | - | ||
| 215 | - // 3bytes: flag fields from PES_packet_length to PES_header_data_length | ||
| 216 | - int pes_size = (last - pos) + header_size + 3; | ||
| 217 | - if (pes_size > 0xffff) { | ||
| 218 | - /** | ||
| 219 | - * when actual packet length > 0xffff(65535), | ||
| 220 | - * which exceed the max u_int16_t packet length, | ||
| 221 | - * use 0 packet length, the next unit start indicates the end of packet. | ||
| 222 | - */ | ||
| 223 | - pes_size = 0; | ||
| 224 | - } | ||
| 225 | - | ||
| 226 | - // PES_packet_length; //16bits | ||
| 227 | - *p++ = (pes_size >> 8); | ||
| 228 | - *p++ = pes_size; | ||
| 229 | - | ||
| 230 | - // PES_scrambling_control; //2bits, '10' | ||
| 231 | - // PES_priority; //1bit | ||
| 232 | - // data_alignment_indicator; //1bit | ||
| 233 | - // copyright; //1bit | ||
| 234 | - // original_or_copy; //1bit | ||
| 235 | - *p++ = 0x80; /* H222 */ | ||
| 236 | - | ||
| 237 | - // PTS_DTS_flags; //2bits | ||
| 238 | - // ESCR_flag; //1bit | ||
| 239 | - // ES_rate_flag; //1bit | ||
| 240 | - // DSM_trick_mode_flag; //1bit | ||
| 241 | - // additional_copy_info_flag; //1bit | ||
| 242 | - // PES_CRC_flag; //1bit | ||
| 243 | - // PES_extension_flag; //1bit | ||
| 244 | - *p++ = flags; | ||
| 245 | - | ||
| 246 | - // PES_header_data_length; //8bits | ||
| 247 | - *p++ = header_size; | ||
| 248 | - | ||
| 249 | - // pts; // 33bits | ||
| 250 | - p = write_pts(p, flags >> 6, frame->pts + SRS_AUTO_HLS_DELAY); | ||
| 251 | - | ||
| 252 | - // dts; // 33bits | ||
| 253 | - if (frame->dts != frame->pts) { | ||
| 254 | - p = write_pts(p, 1, frame->dts + SRS_AUTO_HLS_DELAY); | ||
| 255 | - } | ||
| 256 | - } | ||
| 257 | - | ||
| 258 | - int body_size = sizeof(packet) - (p - packet); | ||
| 259 | - int in_size = last - pos; | ||
| 260 | - | ||
| 261 | - if (body_size <= in_size) { | ||
| 262 | - memcpy(p, pos, body_size); | ||
| 263 | - pos += body_size; | ||
| 264 | - } else { | ||
| 265 | - p = fill_stuff(p, packet, body_size, in_size); | ||
| 266 | - memcpy(p, pos, in_size); | ||
| 267 | - pos = last; | ||
| 268 | - } | ||
| 269 | - | ||
| 270 | - // write ts packet | ||
| 271 | - if ((ret = writer->write(packet, sizeof(packet), NULL)) != ERROR_SUCCESS) { | ||
| 272 | - if (!srs_is_client_gracefully_close(ret)) { | ||
| 273 | - srs_error("write ts file failed. ret=%d", ret); | ||
| 274 | - } | ||
| 275 | - return ret; | ||
| 276 | - } | ||
| 277 | - } | ||
| 278 | - | ||
| 279 | - return ret; | ||
| 280 | - } | ||
| 281 | -private: | ||
| 282 | - static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) | ||
| 283 | - { | ||
| 284 | - char* p = pes_body_end; | ||
| 285 | - | ||
| 286 | - // insert the stuff bytes before PES body | ||
| 287 | - int stuff_size = (body_size - in_size); | ||
| 288 | - | ||
| 289 | - // adaption_field_control; //2bits | ||
| 290 | - if (packet[3] & 0x20) { | ||
| 291 | - // has adaptation | ||
| 292 | - // packet[4]: adaption_field_length | ||
| 293 | - // packet[5]: adaption field data | ||
| 294 | - // base: start of PES body | ||
| 295 | - char* base = &packet[5] + packet[4]; | ||
| 296 | - int len = p - base; | ||
| 297 | - p = (char*)memmove(base + stuff_size, base, len) + len; | ||
| 298 | - // increase the adaption field size. | ||
| 299 | - packet[4] += stuff_size; | ||
| 300 | - | ||
| 301 | - return p; | ||
| 302 | - } | ||
| 303 | - | ||
| 304 | - // create adaption field. | ||
| 305 | - // adaption_field_control; //2bits | ||
| 306 | - packet[3] |= 0x20; | ||
| 307 | - // base: start of PES body | ||
| 308 | - char* base = &packet[4]; | ||
| 309 | - int len = p - base; | ||
| 310 | - p = (char*)memmove(base + stuff_size, base, len) + len; | ||
| 311 | - // adaption_field_length; //8bits | ||
| 312 | - packet[4] = (stuff_size - 1); | ||
| 313 | - if (stuff_size >= 2) { | ||
| 314 | - // adaption field flags. | ||
| 315 | - packet[5] = 0; | ||
| 316 | - // adaption data. | ||
| 317 | - if (stuff_size > 2) { | ||
| 318 | - memset(&packet[6], 0xff, stuff_size - 2); | ||
| 319 | - } | ||
| 320 | - } | ||
| 321 | - | ||
| 322 | - return p; | ||
| 323 | - } | ||
| 324 | - static char* write_pcr(char* p, int64_t pcr) | ||
| 325 | - { | ||
| 326 | - // the pcr=dts-delay | ||
| 327 | - // and the pcr maybe negative | ||
| 328 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/268 | ||
| 329 | - int64_t v = srs_max(0, pcr); | ||
| 330 | - | ||
| 331 | - *p++ = (char) (v >> 25); | ||
| 332 | - *p++ = (char) (v >> 17); | ||
| 333 | - *p++ = (char) (v >> 9); | ||
| 334 | - *p++ = (char) (v >> 1); | ||
| 335 | - *p++ = (char) (v << 7 | 0x7e); | ||
| 336 | - *p++ = 0; | ||
| 337 | - | ||
| 338 | - return p; | ||
| 339 | - } | ||
| 340 | - static char* write_pts(char* p, u_int8_t fb, int64_t pts) | ||
| 341 | - { | ||
| 342 | - int32_t val; | ||
| 343 | - | ||
| 344 | - val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; | ||
| 345 | - *p++ = val; | ||
| 346 | - | ||
| 347 | - val = (((pts >> 15) & 0x7fff) << 1) | 1; | ||
| 348 | - *p++ = (val >> 8); | ||
| 349 | - *p++ = val; | ||
| 350 | - | ||
| 351 | - val = (((pts) & 0x7fff) << 1) | 1; | ||
| 352 | - *p++ = (val >> 8); | ||
| 353 | - *p++ = val; | ||
| 354 | - | ||
| 355 | - return p; | ||
| 356 | - } | ||
| 357 | -}; | ||
| 358 | - | ||
| 359 | -SrsMpegtsFrame::SrsMpegtsFrame() | ||
| 360 | -{ | ||
| 361 | - pts = dts = 0; | ||
| 362 | - pid = sid = cc = 0; | ||
| 363 | - key = false; | ||
| 364 | -} | ||
| 365 | - | ||
| 366 | -SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w) | ||
| 367 | -{ | ||
| 368 | - writer = w; | ||
| 369 | -} | ||
| 370 | - | ||
| 371 | -SrsTSMuxer::~SrsTSMuxer() | ||
| 372 | -{ | ||
| 373 | - close(); | ||
| 374 | -} | ||
| 375 | - | ||
| 376 | -int SrsTSMuxer::open(string _path) | ||
| 377 | -{ | ||
| 378 | - int ret = ERROR_SUCCESS; | ||
| 379 | - | ||
| 380 | - path = _path; | ||
| 381 | - | ||
| 382 | - close(); | ||
| 383 | - | ||
| 384 | - if ((ret = writer->open(path)) != ERROR_SUCCESS) { | ||
| 385 | - return ret; | ||
| 386 | - } | ||
| 387 | - | ||
| 388 | - // write mpegts header | ||
| 389 | - if ((ret = SrsMpegtsWriter::write_header(writer)) != ERROR_SUCCESS) { | ||
| 390 | - return ret; | ||
| 391 | - } | ||
| 392 | - | ||
| 393 | - return ret; | ||
| 394 | -} | ||
| 395 | - | ||
| 396 | -int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) | ||
| 397 | -{ | ||
| 398 | - int ret = ERROR_SUCCESS; | ||
| 399 | - | ||
| 400 | - if ((ret = SrsMpegtsWriter::write_frame(writer, af, ab)) != ERROR_SUCCESS) { | ||
| 401 | - return ret; | ||
| 402 | - } | ||
| 403 | - | ||
| 404 | - return ret; | ||
| 405 | -} | ||
| 406 | - | ||
| 407 | -int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) | ||
| 408 | -{ | ||
| 409 | - int ret = ERROR_SUCCESS; | ||
| 410 | - | ||
| 411 | - if ((ret = SrsMpegtsWriter::write_frame(writer, vf, vb)) != ERROR_SUCCESS) { | ||
| 412 | - return ret; | ||
| 413 | - } | ||
| 414 | - | ||
| 415 | - return ret; | ||
| 416 | -} | ||
| 417 | - | ||
| 418 | -void SrsTSMuxer::close() | ||
| 419 | -{ | ||
| 420 | - writer->close(); | ||
| 421 | -} | ||
| 422 | - | ||
| 423 | -SrsTsAacJitter::SrsTsAacJitter() | ||
| 424 | -{ | ||
| 425 | - base_pts = 0; | ||
| 426 | - nb_samples = 0; | ||
| 427 | - | ||
| 428 | - // TODO: config it, 0 means no adjust | ||
| 429 | - sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; | ||
| 430 | -} | ||
| 431 | - | ||
| 432 | -SrsTsAacJitter::~SrsTsAacJitter() | ||
| 433 | -{ | ||
| 434 | -} | ||
| 435 | - | ||
| 436 | -int64_t SrsTsAacJitter::on_buffer_start(int64_t flv_pts, int sample_rate, int aac_sample_rate) | ||
| 437 | -{ | ||
| 438 | - // use sample rate in flv/RTMP. | ||
| 439 | - int flv_sample_rate = flv_sample_rates[sample_rate & 0x03]; | ||
| 440 | - | ||
| 441 | - // override the sample rate by sequence header | ||
| 442 | - if (aac_sample_rate != __SRS_AAC_SAMPLE_RATE_UNSET) { | ||
| 443 | - flv_sample_rate = aac_sample_rates[aac_sample_rate]; | ||
| 444 | - } | ||
| 445 | - | ||
| 446 | - // sync time set to 0, donot adjust the aac timestamp. | ||
| 447 | - if (!sync_ms) { | ||
| 448 | - return flv_pts; | ||
| 449 | - } | ||
| 450 | - | ||
| 451 | - // @see: ngx_rtmp_hls_audio | ||
| 452 | - // drop the rtmp audio packet timestamp, re-calc it by sample rate. | ||
| 453 | - // | ||
| 454 | - // resample for the tbn of ts is 90000, flv is 1000, | ||
| 455 | - // we will lost timestamp if use audio packet timestamp, | ||
| 456 | - // so we must resample. or audio will corupt in IOS. | ||
| 457 | - int64_t est_pts = base_pts + nb_samples * 90000LL * _SRS_AAC_SAMPLE_SIZE / flv_sample_rate; | ||
| 458 | - int64_t dpts = (int64_t) (est_pts - flv_pts); | ||
| 459 | - | ||
| 460 | - if (dpts <= (int64_t) sync_ms * 90 && dpts >= (int64_t) sync_ms * -90) { | ||
| 461 | - srs_info("HLS correct aac pts " | ||
| 462 | - "from %"PRId64" to %"PRId64", base=%"PRId64", nb_samples=%d, sample_rate=%d", | ||
| 463 | - flv_pts, est_pts, nb_samples, flv_sample_rate, base_pts); | ||
| 464 | - | ||
| 465 | - nb_samples++; | ||
| 466 | - | ||
| 467 | - return est_pts; | ||
| 468 | - } | ||
| 469 | - | ||
| 470 | - // resync | ||
| 471 | - srs_trace("HLS aac resync, dpts=%"PRId64", pts=%"PRId64 | ||
| 472 | - ", base=%"PRId64", nb_samples=%"PRId64", sample_rate=%d", | ||
| 473 | - dpts, flv_pts, base_pts, nb_samples, flv_sample_rate); | ||
| 474 | - | ||
| 475 | - base_pts = flv_pts; | ||
| 476 | - nb_samples = 1; | ||
| 477 | - | ||
| 478 | - return flv_pts; | ||
| 479 | -} | ||
| 480 | - | ||
| 481 | -void SrsTsAacJitter::on_buffer_continue() | ||
| 482 | -{ | ||
| 483 | - nb_samples++; | ||
| 484 | -} | ||
| 485 | - | ||
| 486 | -SrsTsCache::SrsTsCache() | ||
| 487 | -{ | ||
| 488 | - aac_jitter = new SrsTsAacJitter(); | ||
| 489 | - | ||
| 490 | - ab = new SrsSimpleBuffer(); | ||
| 491 | - vb = new SrsSimpleBuffer(); | ||
| 492 | - | ||
| 493 | - af = new SrsMpegtsFrame(); | ||
| 494 | - vf = new SrsMpegtsFrame(); | ||
| 495 | -} | ||
| 496 | - | ||
| 497 | -SrsTsCache::~SrsTsCache() | ||
| 498 | -{ | ||
| 499 | - srs_freep(aac_jitter); | ||
| 500 | - | ||
| 501 | - ab->erase(ab->length()); | ||
| 502 | - vb->erase(vb->length()); | ||
| 503 | - | ||
| 504 | - srs_freep(ab); | ||
| 505 | - srs_freep(vb); | ||
| 506 | - | ||
| 507 | - srs_freep(af); | ||
| 508 | - srs_freep(vf); | ||
| 509 | -} | ||
| 510 | - | ||
| 511 | -int SrsTsCache::cache_audio(SrsAvcAacCodec* codec, int64_t pts, SrsCodecSample* sample) | ||
| 512 | -{ | ||
| 513 | - int ret = ERROR_SUCCESS; | ||
| 514 | - | ||
| 515 | - // start buffer, set the af | ||
| 516 | - if (ab->length() == 0) { | ||
| 517 | - pts = aac_jitter->on_buffer_start(pts, sample->sound_rate, codec->aac_sample_rate); | ||
| 518 | - | ||
| 519 | - af->dts = af->pts = pts; | ||
| 520 | - af->pid = TS_AUDIO_PID; | ||
| 521 | - af->sid = TS_AUDIO_AAC; | ||
| 522 | - } else { | ||
| 523 | - aac_jitter->on_buffer_continue(); | ||
| 524 | - } | ||
| 525 | - | ||
| 526 | - // write audio to cache. | ||
| 527 | - if ((ret = do_cache_audio(codec, sample)) != ERROR_SUCCESS) { | ||
| 528 | - return ret; | ||
| 529 | - } | ||
| 530 | - | ||
| 531 | - return ret; | ||
| 532 | -} | ||
| 533 | - | ||
| 534 | -int SrsTsCache::cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample) | ||
| 535 | -{ | ||
| 536 | - int ret = ERROR_SUCCESS; | ||
| 537 | - | ||
| 538 | - // write video to cache. | ||
| 539 | - if ((ret = do_cache_video(codec, sample)) != ERROR_SUCCESS) { | ||
| 540 | - return ret; | ||
| 541 | - } | ||
| 542 | - | ||
| 543 | - vf->dts = dts; | ||
| 544 | - vf->pts = vf->dts + sample->cts * 90; | ||
| 545 | - vf->pid = TS_VIDEO_PID; | ||
| 546 | - vf->sid = TS_VIDEO_AVC; | ||
| 547 | - vf->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
| 548 | - | ||
| 549 | - return ret; | ||
| 550 | -} | ||
| 551 | - | ||
| 552 | -int SrsTsCache::do_cache_audio(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
| 553 | -{ | ||
| 554 | - int ret = ERROR_SUCCESS; | ||
| 555 | - | ||
| 556 | - for (int i = 0; i < sample->nb_sample_units; i++) { | ||
| 557 | - SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; | ||
| 558 | - int32_t size = sample_unit->size; | ||
| 559 | - | ||
| 560 | - if (!sample_unit->bytes || size <= 0 || size > 0x1fff) { | ||
| 561 | - ret = ERROR_HLS_AAC_FRAME_LENGTH; | ||
| 562 | - srs_error("invalid aac frame length=%d, ret=%d", size, ret); | ||
| 563 | - return ret; | ||
| 564 | - } | ||
| 565 | - | ||
| 566 | - // the frame length is the AAC raw data plus the adts header size. | ||
| 567 | - int32_t frame_length = size + 7; | ||
| 568 | - | ||
| 569 | - // AAC-ADTS | ||
| 570 | - // 6.2 Audio Data Transport Stream, ADTS | ||
| 571 | - // in aac-iso-13818-7.pdf, page 26. | ||
| 572 | - // fixed 7bytes header | ||
| 573 | - static u_int8_t adts_header[7] = {0xff, 0xf1, 0x00, 0x00, 0x00, 0x0f, 0xfc}; | ||
| 574 | - /* | ||
| 575 | - // adts_fixed_header | ||
| 576 | - // 2B, 16bits | ||
| 577 | - int16_t syncword; //12bits, '1111 1111 1111' | ||
| 578 | - int8_t ID; //1bit, '0' | ||
| 579 | - int8_t layer; //2bits, '00' | ||
| 580 | - int8_t protection_absent; //1bit, can be '1' | ||
| 581 | - // 12bits | ||
| 582 | - int8_t profile; //2bit, 7.1 Profiles, page 40 | ||
| 583 | - TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46 | ||
| 584 | - int8_t private_bit; //1bit, can be '0' | ||
| 585 | - int8_t channel_configuration; //3bits, Table 8 | ||
| 586 | - int8_t original_or_copy; //1bit, can be '0' | ||
| 587 | - int8_t home; //1bit, can be '0' | ||
| 588 | - | ||
| 589 | - // adts_variable_header | ||
| 590 | - // 28bits | ||
| 591 | - int8_t copyright_identification_bit; //1bit, can be '0' | ||
| 592 | - int8_t copyright_identification_start; //1bit, can be '0' | ||
| 593 | - int16_t frame_length; //13bits | ||
| 594 | - int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream. | ||
| 595 | - int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block() | ||
| 596 | - */ | ||
| 597 | - // profile, 2bits | ||
| 598 | - adts_header[2] = (codec->aac_profile << 6) & 0xc0; | ||
| 599 | - // sampling_frequency_index 4bits | ||
| 600 | - adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c; | ||
| 601 | - // channel_configuration 3bits | ||
| 602 | - adts_header[2] |= (codec->aac_channels >> 2) & 0x01; | ||
| 603 | - adts_header[3] = (codec->aac_channels << 6) & 0xc0; | ||
| 604 | - // frame_length 13bits | ||
| 605 | - adts_header[3] |= (frame_length >> 11) & 0x03; | ||
| 606 | - adts_header[4] = (frame_length >> 3) & 0xff; | ||
| 607 | - adts_header[5] = ((frame_length << 5) & 0xe0); | ||
| 608 | - // adts_buffer_fullness; //11bits | ||
| 609 | - adts_header[5] |= 0x1f; | ||
| 610 | - | ||
| 611 | - // copy to audio buffer | ||
| 612 | - ab->append((const char*)adts_header, sizeof(adts_header)); | ||
| 613 | - ab->append(sample_unit->bytes, sample_unit->size); | ||
| 614 | - } | ||
| 615 | - | ||
| 616 | - return ret; | ||
| 617 | -} | ||
| 618 | - | ||
| 619 | -int SrsTsCache::do_cache_video(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
| 620 | -{ | ||
| 621 | - int ret = ERROR_SUCCESS; | ||
| 622 | - | ||
| 623 | - // for type1/5/6, insert aud packet. | ||
| 624 | - static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; | ||
| 625 | - | ||
| 626 | - bool sps_pps_sent = false; | ||
| 627 | - bool aud_sent = false; | ||
| 628 | - /** | ||
| 629 | - * a ts sample is format as: | ||
| 630 | - * 00 00 00 01 // header | ||
| 631 | - * xxxxxxx // data bytes | ||
| 632 | - * 00 00 01 // continue header | ||
| 633 | - * xxxxxxx // data bytes. | ||
| 634 | - * so, for each sample, we append header in aud_nal, then appends the bytes in sample. | ||
| 635 | - */ | ||
| 636 | - for (int i = 0; i < sample->nb_sample_units; i++) { | ||
| 637 | - SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; | ||
| 638 | - int32_t size = sample_unit->size; | ||
| 639 | - | ||
| 640 | - if (!sample_unit->bytes || size <= 0) { | ||
| 641 | - ret = ERROR_HLS_AVC_SAMPLE_SIZE; | ||
| 642 | - srs_error("invalid avc sample length=%d, ret=%d", size, ret); | ||
| 643 | - return ret; | ||
| 644 | - } | ||
| 645 | - | ||
| 646 | - /** | ||
| 647 | - * step 1: | ||
| 648 | - * first, before each "real" sample, | ||
| 649 | - * we add some packets according to the nal_unit_type, | ||
| 650 | - * for example, when got nal_unit_type=5, insert SPS/PPS before sample. | ||
| 651 | - */ | ||
| 652 | - | ||
| 653 | - // 5bits, 7.3.1 NAL unit syntax, | ||
| 654 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 655 | - u_int8_t nal_unit_type; | ||
| 656 | - nal_unit_type = *sample_unit->bytes; | ||
| 657 | - nal_unit_type &= 0x1f; | ||
| 658 | - | ||
| 659 | - // @see: ngx_rtmp_hls_video | ||
| 660 | - // Table 7-1 C NAL unit type codes, page 61 | ||
| 661 | - // 1: Coded slice | ||
| 662 | - if (nal_unit_type == 1) { | ||
| 663 | - sps_pps_sent = false; | ||
| 664 | - } | ||
| 665 | - | ||
| 666 | - // 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61 | ||
| 667 | - // @see: ngx_rtmp_hls_append_aud | ||
| 668 | - if (!aud_sent) { | ||
| 669 | - // @remark, when got type 9, we donot send aud_nal, but it will make | ||
| 670 | - // ios unhappy, so we remove it. | ||
| 671 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/281 | ||
| 672 | - /*if (nal_unit_type == 9) { | ||
| 673 | - aud_sent = true; | ||
| 674 | - }*/ | ||
| 675 | - | ||
| 676 | - if (nal_unit_type == 1 || nal_unit_type == 5 || nal_unit_type == 6) { | ||
| 677 | - // for type 6, append a aud with type 9. | ||
| 678 | - vb->append((const char*)aud_nal, sizeof(aud_nal)); | ||
| 679 | - aud_sent = true; | ||
| 680 | - } | ||
| 681 | - } | ||
| 682 | - | ||
| 683 | - // 5: Coded slice of an IDR picture. | ||
| 684 | - // insert sps/pps before IDR or key frame is ok. | ||
| 685 | - if (nal_unit_type == 5 && !sps_pps_sent) { | ||
| 686 | - sps_pps_sent = true; | ||
| 687 | - | ||
| 688 | - // @see: ngx_rtmp_hls_append_sps_pps | ||
| 689 | - if (codec->sequenceParameterSetLength > 0) { | ||
| 690 | - // AnnexB prefix, for sps always 4 bytes header | ||
| 691 | - vb->append((const char*)aud_nal, 4); | ||
| 692 | - // sps | ||
| 693 | - vb->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); | ||
| 694 | - } | ||
| 695 | - if (codec->pictureParameterSetLength > 0) { | ||
| 696 | - // AnnexB prefix, for pps always 4 bytes header | ||
| 697 | - vb->append((const char*)aud_nal, 4); | ||
| 698 | - // pps | ||
| 699 | - vb->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); | ||
| 700 | - } | ||
| 701 | - } | ||
| 702 | - | ||
| 703 | - // 7-9, ignore, @see: ngx_rtmp_hls_video | ||
| 704 | - if (nal_unit_type >= 7 && nal_unit_type <= 9) { | ||
| 705 | - continue; | ||
| 706 | - } | ||
| 707 | - | ||
| 708 | - /** | ||
| 709 | - * step 2: | ||
| 710 | - * output the "real" sample, in buf. | ||
| 711 | - * when we output some special assist packets according to nal_unit_type | ||
| 712 | - */ | ||
| 713 | - | ||
| 714 | - // sample start prefix, '00 00 00 01' or '00 00 01' | ||
| 715 | - u_int8_t* p = aud_nal + 1; | ||
| 716 | - u_int8_t* end = p + 3; | ||
| 717 | - | ||
| 718 | - // first AnnexB prefix is long (4 bytes) | ||
| 719 | - if (vb->length() == 0) { | ||
| 720 | - p = aud_nal; | ||
| 721 | - } | ||
| 722 | - vb->append((const char*)p, end - p); | ||
| 723 | - | ||
| 724 | - // sample data | ||
| 725 | - vb->append(sample_unit->bytes, sample_unit->size); | ||
| 726 | - } | ||
| 727 | - | ||
| 728 | - return ret; | ||
| 729 | -} | ||
| 730 | - | ||
| 731 | SrsCodecSampleUnit::SrsCodecSampleUnit() | 35 | SrsCodecSampleUnit::SrsCodecSampleUnit() |
| 732 | { | 36 | { |
| 733 | size = 0; | 37 | size = 0; |
| @@ -95,109 +95,6 @@ enum SrsCodecAudioSoundType | @@ -95,109 +95,6 @@ enum SrsCodecAudioSoundType | ||
| 95 | SrsCodecAudioSoundTypeStereo = 1, | 95 | SrsCodecAudioSoundTypeStereo = 1, |
| 96 | }; | 96 | }; |
| 97 | 97 | ||
| 98 | -// @see: ngx_rtmp_SrsMpegtsFrame_t | ||
| 99 | -class SrsMpegtsFrame | ||
| 100 | -{ | ||
| 101 | -public: | ||
| 102 | - int64_t pts; | ||
| 103 | - int64_t dts; | ||
| 104 | - int pid; | ||
| 105 | - int sid; | ||
| 106 | - int cc; | ||
| 107 | - bool key; | ||
| 108 | - | ||
| 109 | - SrsMpegtsFrame(); | ||
| 110 | -}; | ||
| 111 | - | ||
| 112 | -/** | ||
| 113 | -* write data from frame(header info) and buffer(data) to ts file. | ||
| 114 | -* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter | ||
| 115 | -*/ | ||
| 116 | -class SrsTSMuxer | ||
| 117 | -{ | ||
| 118 | -private: | ||
| 119 | - SrsFileWriter* writer; | ||
| 120 | - std::string path; | ||
| 121 | -public: | ||
| 122 | - SrsTSMuxer(SrsFileWriter* w); | ||
| 123 | - virtual ~SrsTSMuxer(); | ||
| 124 | -public: | ||
| 125 | - virtual int open(std::string _path); | ||
| 126 | - virtual int write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); | ||
| 127 | - virtual int write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); | ||
| 128 | - virtual void close(); | ||
| 129 | -}; | ||
| 130 | - | ||
| 131 | -/** | ||
| 132 | -* jitter correct for audio, | ||
| 133 | -* the sample rate 44100/32000 will lost precise, | ||
| 134 | -* when mp4/ts(tbn=90000) covert to flv/rtmp(1000), | ||
| 135 | -* so the Hls on ipad or iphone will corrupt, | ||
| 136 | -* @see nginx-rtmp: est_pts | ||
| 137 | -*/ | ||
| 138 | -class SrsTsAacJitter | ||
| 139 | -{ | ||
| 140 | -private: | ||
| 141 | - int64_t base_pts; | ||
| 142 | - int64_t nb_samples; | ||
| 143 | - int sync_ms; | ||
| 144 | -public: | ||
| 145 | - SrsTsAacJitter(); | ||
| 146 | - virtual ~SrsTsAacJitter(); | ||
| 147 | - /** | ||
| 148 | - * when buffer start, calc the "correct" pts for ts, | ||
| 149 | - * @param flv_pts, the flv pts calc from flv header timestamp, | ||
| 150 | - * @param sample_rate, the sample rate in format(flv/RTMP packet header). | ||
| 151 | - * @param aac_sample_rate, the sample rate in codec(sequence header). | ||
| 152 | - * @return the calc correct pts. | ||
| 153 | - */ | ||
| 154 | - virtual int64_t on_buffer_start(int64_t flv_pts, int sample_rate, int aac_sample_rate); | ||
| 155 | - /** | ||
| 156 | - * when buffer continue, muxer donot write to file, | ||
| 157 | - * the audio buffer continue grow and donot need a pts, | ||
| 158 | - * for the ts audio PES packet only has one pts at the first time. | ||
| 159 | - */ | ||
| 160 | - virtual void on_buffer_continue(); | ||
| 161 | -}; | ||
| 162 | - | ||
| 163 | -/** | ||
| 164 | -* ts stream cache, | ||
| 165 | -* use to cache ts stream. | ||
| 166 | -* | ||
| 167 | -* about the flv tbn problem: | ||
| 168 | -* flv tbn is 1/1000, ts tbn is 1/90000, | ||
| 169 | -* when timestamp convert to flv tbn, it will loose precise, | ||
| 170 | -* so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter, | ||
| 171 | -* we use a aac jitter to correct the audio pts. | ||
| 172 | -*/ | ||
| 173 | -class SrsTsCache | ||
| 174 | -{ | ||
| 175 | -public: | ||
| 176 | - // current frame and buffer | ||
| 177 | - SrsMpegtsFrame* af; | ||
| 178 | - SrsSimpleBuffer* ab; | ||
| 179 | - SrsMpegtsFrame* vf; | ||
| 180 | - SrsSimpleBuffer* vb; | ||
| 181 | -protected: | ||
| 182 | - // time jitter for aac | ||
| 183 | - SrsTsAacJitter* aac_jitter; | ||
| 184 | -public: | ||
| 185 | - SrsTsCache(); | ||
| 186 | - virtual ~SrsTsCache(); | ||
| 187 | -public: | ||
| 188 | - /** | ||
| 189 | - * write audio to cache | ||
| 190 | - */ | ||
| 191 | - virtual int cache_audio(SrsAvcAacCodec* codec, int64_t pts, SrsCodecSample* sample); | ||
| 192 | - /** | ||
| 193 | - * write video to muxer. | ||
| 194 | - */ | ||
| 195 | - virtual int cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample); | ||
| 196 | -private: | ||
| 197 | - virtual int do_cache_audio(SrsAvcAacCodec* codec, SrsCodecSample* sample); | ||
| 198 | - virtual int do_cache_video(SrsAvcAacCodec* codec, SrsCodecSample* sample); | ||
| 199 | -}; | ||
| 200 | - | ||
| 201 | /** | 98 | /** |
| 202 | * the codec sample unit. | 99 | * the codec sample unit. |
| 203 | * for h.264 video packet, a NALU is a sample unit. | 100 | * for h.264 video packet, a NALU is a sample unit. |
| @@ -37,6 +37,703 @@ using namespace std; | @@ -37,6 +37,703 @@ using namespace std; | ||
| 37 | #include <srs_kernel_file.hpp> | 37 | #include <srs_kernel_file.hpp> |
| 38 | #include <srs_kernel_avc.hpp> | 38 | #include <srs_kernel_avc.hpp> |
| 39 | #include <srs_kernel_buffer.hpp> | 39 | #include <srs_kernel_buffer.hpp> |
| 40 | +#include <srs_kernel_utility.hpp> | ||
| 41 | + | ||
| 42 | +// in ms, for HLS aac sync time. | ||
| 43 | +#define SRS_CONF_DEFAULT_AAC_SYNC 100 | ||
| 44 | + | ||
| 45 | +// @see: ngx_rtmp_hls_audio | ||
| 46 | +/* We assume here AAC frame size is 1024 | ||
| 47 | + * Need to handle AAC frames with frame size of 960 */ | ||
| 48 | +#define _SRS_AAC_SAMPLE_SIZE 1024 | ||
| 49 | + | ||
| 50 | +// the mpegts header specifed the video/audio pid. | ||
| 51 | +#define TS_VIDEO_PID 256 | ||
| 52 | +#define TS_AUDIO_PID 257 | ||
| 53 | + | ||
| 54 | +// ts aac stream id. | ||
| 55 | +#define TS_AUDIO_AAC 0xc0 | ||
| 56 | +// ts avc stream id. | ||
| 57 | +#define TS_VIDEO_AVC 0xe0 | ||
| 58 | + | ||
| 59 | +/** | ||
| 60 | +* the public data, event HLS disable, others can use it. | ||
| 61 | +*/ | ||
| 62 | +// 0 = 5.5 kHz = 5512 Hz | ||
| 63 | +// 1 = 11 kHz = 11025 Hz | ||
| 64 | +// 2 = 22 kHz = 22050 Hz | ||
| 65 | +// 3 = 44 kHz = 44100 Hz | ||
| 66 | +int flv_sample_rates[] = {5512, 11025, 22050, 44100}; | ||
| 67 | + | ||
| 68 | +// the sample rates in the codec, | ||
| 69 | +// in the sequence header. | ||
| 70 | +int aac_sample_rates[] = | ||
| 71 | +{ | ||
| 72 | + 96000, 88200, 64000, 48000, | ||
| 73 | + 44100, 32000, 24000, 22050, | ||
| 74 | + 16000, 12000, 11025, 8000, | ||
| 75 | + 7350, 0, 0, 0 | ||
| 76 | +}; | ||
| 77 | + | ||
| 78 | +// @see: NGX_RTMP_HLS_DELAY, | ||
| 79 | +// 63000: 700ms, ts_tbn=90000 | ||
| 80 | +#define SRS_AUTO_HLS_DELAY 63000 | ||
| 81 | + | ||
| 82 | +// @see: ngx_rtmp_mpegts_header | ||
| 83 | +u_int8_t mpegts_header[] = { | ||
| 84 | + /* TS */ | ||
| 85 | + 0x47, 0x40, 0x00, 0x10, 0x00, | ||
| 86 | + /* PSI */ | ||
| 87 | + 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
| 88 | + /* PAT */ | ||
| 89 | + 0x00, 0x01, 0xf0, 0x01, | ||
| 90 | + /* CRC */ | ||
| 91 | + 0x2e, 0x70, 0x19, 0x05, | ||
| 92 | + /* stuffing 167 bytes */ | ||
| 93 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 94 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 95 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 96 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 97 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 98 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 99 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 100 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 101 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 102 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 103 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 104 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 105 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 106 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 107 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 108 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 109 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 110 | + | ||
| 111 | + /* TS */ | ||
| 112 | + 0x47, 0x50, 0x01, 0x10, 0x00, | ||
| 113 | + /* PSI */ | ||
| 114 | + 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
| 115 | + /* PMT */ | ||
| 116 | + 0xe1, 0x00, | ||
| 117 | + 0xf0, 0x00, | ||
| 118 | + // must generate header with/without video, @see: | ||
| 119 | + // https://github.com/winlinvip/simple-rtmp-server/issues/40 | ||
| 120 | + 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ | ||
| 121 | + 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ | ||
| 122 | + /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ | ||
| 123 | + /* CRC */ | ||
| 124 | + 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ | ||
| 125 | + /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ | ||
| 126 | + /* stuffing 157 bytes */ | ||
| 127 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 128 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 129 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 130 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 131 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 132 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 133 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 134 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 135 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 136 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 137 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 138 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 139 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 140 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 141 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 142 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | ||
| 143 | +}; | ||
| 144 | + | ||
| 145 | +// @see: ngx_rtmp_mpegts.c | ||
| 146 | +// TODO: support full mpegts feature in future. | ||
| 147 | +class SrsMpegtsWriter | ||
| 148 | +{ | ||
| 149 | +public: | ||
| 150 | + static int write_header(SrsFileWriter* writer) | ||
| 151 | + { | ||
| 152 | + int ret = ERROR_SUCCESS; | ||
| 153 | + | ||
| 154 | + if ((ret = writer->write(mpegts_header, sizeof(mpegts_header), NULL)) != ERROR_SUCCESS) { | ||
| 155 | + ret = ERROR_HLS_WRITE_FAILED; | ||
| 156 | + srs_error("write ts file header failed. ret=%d", ret); | ||
| 157 | + return ret; | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + return ret; | ||
| 161 | + } | ||
| 162 | + static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer) | ||
| 163 | + { | ||
| 164 | + int ret = ERROR_SUCCESS; | ||
| 165 | + | ||
| 166 | + if (!buffer->bytes() || buffer->length() <= 0) { | ||
| 167 | + return ret; | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + char* last = buffer->bytes() + buffer->length(); | ||
| 171 | + char* pos = buffer->bytes(); | ||
| 172 | + | ||
| 173 | + bool first = true; | ||
| 174 | + while (pos < last) { | ||
| 175 | + static char packet[188]; | ||
| 176 | + char* p = packet; | ||
| 177 | + | ||
| 178 | + frame->cc++; | ||
| 179 | + | ||
| 180 | + // sync_byte; //8bits | ||
| 181 | + *p++ = 0x47; | ||
| 182 | + // pid; //13bits | ||
| 183 | + *p++ = (frame->pid >> 8) & 0x1f; | ||
| 184 | + // payload_unit_start_indicator; //1bit | ||
| 185 | + if (first) { | ||
| 186 | + p[-1] |= 0x40; | ||
| 187 | + } | ||
| 188 | + *p++ = frame->pid; | ||
| 189 | + | ||
| 190 | + // transport_scrambling_control; //2bits | ||
| 191 | + // adaption_field_control; //2bits, 0x01: PayloadOnly | ||
| 192 | + // continuity_counter; //4bits | ||
| 193 | + *p++ = 0x10 | (frame->cc & 0x0f); | ||
| 194 | + | ||
| 195 | + if (first) { | ||
| 196 | + first = false; | ||
| 197 | + if (frame->key) { | ||
| 198 | + p[-1] |= 0x20; // Both Adaption and Payload | ||
| 199 | + *p++ = 7; // size | ||
| 200 | + *p++ = 0x50; // random access + PCR | ||
| 201 | + p = write_pcr(p, frame->dts - SRS_AUTO_HLS_DELAY); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + // PES header | ||
| 205 | + // packet_start_code_prefix; //24bits, '00 00 01' | ||
| 206 | + *p++ = 0x00; | ||
| 207 | + *p++ = 0x00; | ||
| 208 | + *p++ = 0x01; | ||
| 209 | + //8bits | ||
| 210 | + *p++ = frame->sid; | ||
| 211 | + | ||
| 212 | + // pts(33bits) need 5bytes. | ||
| 213 | + u_int8_t header_size = 5; | ||
| 214 | + u_int8_t flags = 0x80; // pts | ||
| 215 | + | ||
| 216 | + // dts(33bits) need 5bytes also | ||
| 217 | + if (frame->dts != frame->pts) { | ||
| 218 | + header_size += 5; | ||
| 219 | + flags |= 0x40; // dts | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + // 3bytes: flag fields from PES_packet_length to PES_header_data_length | ||
| 223 | + int pes_size = (last - pos) + header_size + 3; | ||
| 224 | + if (pes_size > 0xffff) { | ||
| 225 | + /** | ||
| 226 | + * when actual packet length > 0xffff(65535), | ||
| 227 | + * which exceed the max u_int16_t packet length, | ||
| 228 | + * use 0 packet length, the next unit start indicates the end of packet. | ||
| 229 | + */ | ||
| 230 | + pes_size = 0; | ||
| 231 | + } | ||
| 232 | + | ||
| 233 | + // PES_packet_length; //16bits | ||
| 234 | + *p++ = (pes_size >> 8); | ||
| 235 | + *p++ = pes_size; | ||
| 236 | + | ||
| 237 | + // PES_scrambling_control; //2bits, '10' | ||
| 238 | + // PES_priority; //1bit | ||
| 239 | + // data_alignment_indicator; //1bit | ||
| 240 | + // copyright; //1bit | ||
| 241 | + // original_or_copy; //1bit | ||
| 242 | + *p++ = 0x80; /* H222 */ | ||
| 243 | + | ||
| 244 | + // PTS_DTS_flags; //2bits | ||
| 245 | + // ESCR_flag; //1bit | ||
| 246 | + // ES_rate_flag; //1bit | ||
| 247 | + // DSM_trick_mode_flag; //1bit | ||
| 248 | + // additional_copy_info_flag; //1bit | ||
| 249 | + // PES_CRC_flag; //1bit | ||
| 250 | + // PES_extension_flag; //1bit | ||
| 251 | + *p++ = flags; | ||
| 252 | + | ||
| 253 | + // PES_header_data_length; //8bits | ||
| 254 | + *p++ = header_size; | ||
| 255 | + | ||
| 256 | + // pts; // 33bits | ||
| 257 | + p = write_pts(p, flags >> 6, frame->pts + SRS_AUTO_HLS_DELAY); | ||
| 258 | + | ||
| 259 | + // dts; // 33bits | ||
| 260 | + if (frame->dts != frame->pts) { | ||
| 261 | + p = write_pts(p, 1, frame->dts + SRS_AUTO_HLS_DELAY); | ||
| 262 | + } | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + int body_size = sizeof(packet) - (p - packet); | ||
| 266 | + int in_size = last - pos; | ||
| 267 | + | ||
| 268 | + if (body_size <= in_size) { | ||
| 269 | + memcpy(p, pos, body_size); | ||
| 270 | + pos += body_size; | ||
| 271 | + } else { | ||
| 272 | + p = fill_stuff(p, packet, body_size, in_size); | ||
| 273 | + memcpy(p, pos, in_size); | ||
| 274 | + pos = last; | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + // write ts packet | ||
| 278 | + if ((ret = writer->write(packet, sizeof(packet), NULL)) != ERROR_SUCCESS) { | ||
| 279 | + if (!srs_is_client_gracefully_close(ret)) { | ||
| 280 | + srs_error("write ts file failed. ret=%d", ret); | ||
| 281 | + } | ||
| 282 | + return ret; | ||
| 283 | + } | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + return ret; | ||
| 287 | + } | ||
| 288 | +private: | ||
| 289 | + static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) | ||
| 290 | + { | ||
| 291 | + char* p = pes_body_end; | ||
| 292 | + | ||
| 293 | + // insert the stuff bytes before PES body | ||
| 294 | + int stuff_size = (body_size - in_size); | ||
| 295 | + | ||
| 296 | + // adaption_field_control; //2bits | ||
| 297 | + if (packet[3] & 0x20) { | ||
| 298 | + // has adaptation | ||
| 299 | + // packet[4]: adaption_field_length | ||
| 300 | + // packet[5]: adaption field data | ||
| 301 | + // base: start of PES body | ||
| 302 | + char* base = &packet[5] + packet[4]; | ||
| 303 | + int len = p - base; | ||
| 304 | + p = (char*)memmove(base + stuff_size, base, len) + len; | ||
| 305 | + // increase the adaption field size. | ||
| 306 | + packet[4] += stuff_size; | ||
| 307 | + | ||
| 308 | + return p; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + // create adaption field. | ||
| 312 | + // adaption_field_control; //2bits | ||
| 313 | + packet[3] |= 0x20; | ||
| 314 | + // base: start of PES body | ||
| 315 | + char* base = &packet[4]; | ||
| 316 | + int len = p - base; | ||
| 317 | + p = (char*)memmove(base + stuff_size, base, len) + len; | ||
| 318 | + // adaption_field_length; //8bits | ||
| 319 | + packet[4] = (stuff_size - 1); | ||
| 320 | + if (stuff_size >= 2) { | ||
| 321 | + // adaption field flags. | ||
| 322 | + packet[5] = 0; | ||
| 323 | + // adaption data. | ||
| 324 | + if (stuff_size > 2) { | ||
| 325 | + memset(&packet[6], 0xff, stuff_size - 2); | ||
| 326 | + } | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + return p; | ||
| 330 | + } | ||
| 331 | + static char* write_pcr(char* p, int64_t pcr) | ||
| 332 | + { | ||
| 333 | + // the pcr=dts-delay | ||
| 334 | + // and the pcr maybe negative | ||
| 335 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/268 | ||
| 336 | + int64_t v = srs_max(0, pcr); | ||
| 337 | + | ||
| 338 | + *p++ = (char) (v >> 25); | ||
| 339 | + *p++ = (char) (v >> 17); | ||
| 340 | + *p++ = (char) (v >> 9); | ||
| 341 | + *p++ = (char) (v >> 1); | ||
| 342 | + *p++ = (char) (v << 7 | 0x7e); | ||
| 343 | + *p++ = 0; | ||
| 344 | + | ||
| 345 | + return p; | ||
| 346 | + } | ||
| 347 | + static char* write_pts(char* p, u_int8_t fb, int64_t pts) | ||
| 348 | + { | ||
| 349 | + int32_t val; | ||
| 350 | + | ||
| 351 | + val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; | ||
| 352 | + *p++ = val; | ||
| 353 | + | ||
| 354 | + val = (((pts >> 15) & 0x7fff) << 1) | 1; | ||
| 355 | + *p++ = (val >> 8); | ||
| 356 | + *p++ = val; | ||
| 357 | + | ||
| 358 | + val = (((pts) & 0x7fff) << 1) | 1; | ||
| 359 | + *p++ = (val >> 8); | ||
| 360 | + *p++ = val; | ||
| 361 | + | ||
| 362 | + return p; | ||
| 363 | + } | ||
| 364 | +}; | ||
| 365 | + | ||
| 366 | +SrsMpegtsFrame::SrsMpegtsFrame() | ||
| 367 | +{ | ||
| 368 | + pts = dts = 0; | ||
| 369 | + pid = sid = cc = 0; | ||
| 370 | + key = false; | ||
| 371 | +} | ||
| 372 | + | ||
| 373 | +SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w) | ||
| 374 | +{ | ||
| 375 | + writer = w; | ||
| 376 | +} | ||
| 377 | + | ||
| 378 | +SrsTSMuxer::~SrsTSMuxer() | ||
| 379 | +{ | ||
| 380 | + close(); | ||
| 381 | +} | ||
| 382 | + | ||
| 383 | +int SrsTSMuxer::open(string _path) | ||
| 384 | +{ | ||
| 385 | + int ret = ERROR_SUCCESS; | ||
| 386 | + | ||
| 387 | + path = _path; | ||
| 388 | + | ||
| 389 | + close(); | ||
| 390 | + | ||
| 391 | + if ((ret = writer->open(path)) != ERROR_SUCCESS) { | ||
| 392 | + return ret; | ||
| 393 | + } | ||
| 394 | + | ||
| 395 | + // write mpegts header | ||
| 396 | + if ((ret = SrsMpegtsWriter::write_header(writer)) != ERROR_SUCCESS) { | ||
| 397 | + return ret; | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + return ret; | ||
| 401 | +} | ||
| 402 | + | ||
| 403 | +int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) | ||
| 404 | +{ | ||
| 405 | + int ret = ERROR_SUCCESS; | ||
| 406 | + | ||
| 407 | + if ((ret = SrsMpegtsWriter::write_frame(writer, af, ab)) != ERROR_SUCCESS) { | ||
| 408 | + return ret; | ||
| 409 | + } | ||
| 410 | + | ||
| 411 | + return ret; | ||
| 412 | +} | ||
| 413 | + | ||
| 414 | +int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) | ||
| 415 | +{ | ||
| 416 | + int ret = ERROR_SUCCESS; | ||
| 417 | + | ||
| 418 | + if ((ret = SrsMpegtsWriter::write_frame(writer, vf, vb)) != ERROR_SUCCESS) { | ||
| 419 | + return ret; | ||
| 420 | + } | ||
| 421 | + | ||
| 422 | + return ret; | ||
| 423 | +} | ||
| 424 | + | ||
| 425 | +void SrsTSMuxer::close() | ||
| 426 | +{ | ||
| 427 | + writer->close(); | ||
| 428 | +} | ||
| 429 | + | ||
| 430 | +SrsTsAacJitter::SrsTsAacJitter() | ||
| 431 | +{ | ||
| 432 | + base_pts = 0; | ||
| 433 | + nb_samples = 0; | ||
| 434 | + | ||
| 435 | + // TODO: config it, 0 means no adjust | ||
| 436 | + sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; | ||
| 437 | +} | ||
| 438 | + | ||
| 439 | +SrsTsAacJitter::~SrsTsAacJitter() | ||
| 440 | +{ | ||
| 441 | +} | ||
| 442 | + | ||
| 443 | +int64_t SrsTsAacJitter::on_buffer_start(int64_t flv_pts, int sample_rate, int aac_sample_rate) | ||
| 444 | +{ | ||
| 445 | + // use sample rate in flv/RTMP. | ||
| 446 | + int flv_sample_rate = flv_sample_rates[sample_rate & 0x03]; | ||
| 447 | + | ||
| 448 | + // override the sample rate by sequence header | ||
| 449 | + if (aac_sample_rate != __SRS_AAC_SAMPLE_RATE_UNSET) { | ||
| 450 | + flv_sample_rate = aac_sample_rates[aac_sample_rate]; | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + // sync time set to 0, donot adjust the aac timestamp. | ||
| 454 | + if (!sync_ms) { | ||
| 455 | + return flv_pts; | ||
| 456 | + } | ||
| 457 | + | ||
| 458 | + // @see: ngx_rtmp_hls_audio | ||
| 459 | + // drop the rtmp audio packet timestamp, re-calc it by sample rate. | ||
| 460 | + // | ||
| 461 | + // resample for the tbn of ts is 90000, flv is 1000, | ||
| 462 | + // we will lost timestamp if use audio packet timestamp, | ||
| 463 | + // so we must resample. or audio will corupt in IOS. | ||
| 464 | + int64_t est_pts = base_pts + nb_samples * 90000LL * _SRS_AAC_SAMPLE_SIZE / flv_sample_rate; | ||
| 465 | + int64_t dpts = (int64_t) (est_pts - flv_pts); | ||
| 466 | + | ||
| 467 | + if (dpts <= (int64_t) sync_ms * 90 && dpts >= (int64_t) sync_ms * -90) { | ||
| 468 | + srs_info("HLS correct aac pts " | ||
| 469 | + "from %"PRId64" to %"PRId64", base=%"PRId64", nb_samples=%d, sample_rate=%d", | ||
| 470 | + flv_pts, est_pts, nb_samples, flv_sample_rate, base_pts); | ||
| 471 | + | ||
| 472 | + nb_samples++; | ||
| 473 | + | ||
| 474 | + return est_pts; | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + // resync | ||
| 478 | + srs_trace("HLS aac resync, dpts=%"PRId64", pts=%"PRId64 | ||
| 479 | + ", base=%"PRId64", nb_samples=%"PRId64", sample_rate=%d", | ||
| 480 | + dpts, flv_pts, base_pts, nb_samples, flv_sample_rate); | ||
| 481 | + | ||
| 482 | + base_pts = flv_pts; | ||
| 483 | + nb_samples = 1; | ||
| 484 | + | ||
| 485 | + return flv_pts; | ||
| 486 | +} | ||
| 487 | + | ||
| 488 | +void SrsTsAacJitter::on_buffer_continue() | ||
| 489 | +{ | ||
| 490 | + nb_samples++; | ||
| 491 | +} | ||
| 492 | + | ||
| 493 | +SrsTsCache::SrsTsCache() | ||
| 494 | +{ | ||
| 495 | + aac_jitter = new SrsTsAacJitter(); | ||
| 496 | + | ||
| 497 | + ab = new SrsSimpleBuffer(); | ||
| 498 | + vb = new SrsSimpleBuffer(); | ||
| 499 | + | ||
| 500 | + af = new SrsMpegtsFrame(); | ||
| 501 | + vf = new SrsMpegtsFrame(); | ||
| 502 | +} | ||
| 503 | + | ||
| 504 | +SrsTsCache::~SrsTsCache() | ||
| 505 | +{ | ||
| 506 | + srs_freep(aac_jitter); | ||
| 507 | + | ||
| 508 | + ab->erase(ab->length()); | ||
| 509 | + vb->erase(vb->length()); | ||
| 510 | + | ||
| 511 | + srs_freep(ab); | ||
| 512 | + srs_freep(vb); | ||
| 513 | + | ||
| 514 | + srs_freep(af); | ||
| 515 | + srs_freep(vf); | ||
| 516 | +} | ||
| 517 | + | ||
| 518 | +int SrsTsCache::cache_audio(SrsAvcAacCodec* codec, int64_t pts, SrsCodecSample* sample) | ||
| 519 | +{ | ||
| 520 | + int ret = ERROR_SUCCESS; | ||
| 521 | + | ||
| 522 | + // start buffer, set the af | ||
| 523 | + if (ab->length() == 0) { | ||
| 524 | + pts = aac_jitter->on_buffer_start(pts, sample->sound_rate, codec->aac_sample_rate); | ||
| 525 | + | ||
| 526 | + af->dts = af->pts = pts; | ||
| 527 | + af->pid = TS_AUDIO_PID; | ||
| 528 | + af->sid = TS_AUDIO_AAC; | ||
| 529 | + } else { | ||
| 530 | + aac_jitter->on_buffer_continue(); | ||
| 531 | + } | ||
| 532 | + | ||
| 533 | + // write audio to cache. | ||
| 534 | + if ((ret = do_cache_audio(codec, sample)) != ERROR_SUCCESS) { | ||
| 535 | + return ret; | ||
| 536 | + } | ||
| 537 | + | ||
| 538 | + return ret; | ||
| 539 | +} | ||
| 540 | + | ||
| 541 | +int SrsTsCache::cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample) | ||
| 542 | +{ | ||
| 543 | + int ret = ERROR_SUCCESS; | ||
| 544 | + | ||
| 545 | + // write video to cache. | ||
| 546 | + if ((ret = do_cache_video(codec, sample)) != ERROR_SUCCESS) { | ||
| 547 | + return ret; | ||
| 548 | + } | ||
| 549 | + | ||
| 550 | + vf->dts = dts; | ||
| 551 | + vf->pts = vf->dts + sample->cts * 90; | ||
| 552 | + vf->pid = TS_VIDEO_PID; | ||
| 553 | + vf->sid = TS_VIDEO_AVC; | ||
| 554 | + vf->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
| 555 | + | ||
| 556 | + return ret; | ||
| 557 | +} | ||
| 558 | + | ||
| 559 | +int SrsTsCache::do_cache_audio(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
| 560 | +{ | ||
| 561 | + int ret = ERROR_SUCCESS; | ||
| 562 | + | ||
| 563 | + for (int i = 0; i < sample->nb_sample_units; i++) { | ||
| 564 | + SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; | ||
| 565 | + int32_t size = sample_unit->size; | ||
| 566 | + | ||
| 567 | + if (!sample_unit->bytes || size <= 0 || size > 0x1fff) { | ||
| 568 | + ret = ERROR_HLS_AAC_FRAME_LENGTH; | ||
| 569 | + srs_error("invalid aac frame length=%d, ret=%d", size, ret); | ||
| 570 | + return ret; | ||
| 571 | + } | ||
| 572 | + | ||
| 573 | + // the frame length is the AAC raw data plus the adts header size. | ||
| 574 | + int32_t frame_length = size + 7; | ||
| 575 | + | ||
| 576 | + // AAC-ADTS | ||
| 577 | + // 6.2 Audio Data Transport Stream, ADTS | ||
| 578 | + // in aac-iso-13818-7.pdf, page 26. | ||
| 579 | + // fixed 7bytes header | ||
| 580 | + static u_int8_t adts_header[7] = {0xff, 0xf1, 0x00, 0x00, 0x00, 0x0f, 0xfc}; | ||
| 581 | + /* | ||
| 582 | + // adts_fixed_header | ||
| 583 | + // 2B, 16bits | ||
| 584 | + int16_t syncword; //12bits, '1111 1111 1111' | ||
| 585 | + int8_t ID; //1bit, '0' | ||
| 586 | + int8_t layer; //2bits, '00' | ||
| 587 | + int8_t protection_absent; //1bit, can be '1' | ||
| 588 | + // 12bits | ||
| 589 | + int8_t profile; //2bit, 7.1 Profiles, page 40 | ||
| 590 | + TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46 | ||
| 591 | + int8_t private_bit; //1bit, can be '0' | ||
| 592 | + int8_t channel_configuration; //3bits, Table 8 | ||
| 593 | + int8_t original_or_copy; //1bit, can be '0' | ||
| 594 | + int8_t home; //1bit, can be '0' | ||
| 595 | + | ||
| 596 | + // adts_variable_header | ||
| 597 | + // 28bits | ||
| 598 | + int8_t copyright_identification_bit; //1bit, can be '0' | ||
| 599 | + int8_t copyright_identification_start; //1bit, can be '0' | ||
| 600 | + int16_t frame_length; //13bits | ||
| 601 | + int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream. | ||
| 602 | + int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block() | ||
| 603 | + */ | ||
| 604 | + // profile, 2bits | ||
| 605 | + adts_header[2] = (codec->aac_profile << 6) & 0xc0; | ||
| 606 | + // sampling_frequency_index 4bits | ||
| 607 | + adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c; | ||
| 608 | + // channel_configuration 3bits | ||
| 609 | + adts_header[2] |= (codec->aac_channels >> 2) & 0x01; | ||
| 610 | + adts_header[3] = (codec->aac_channels << 6) & 0xc0; | ||
| 611 | + // frame_length 13bits | ||
| 612 | + adts_header[3] |= (frame_length >> 11) & 0x03; | ||
| 613 | + adts_header[4] = (frame_length >> 3) & 0xff; | ||
| 614 | + adts_header[5] = ((frame_length << 5) & 0xe0); | ||
| 615 | + // adts_buffer_fullness; //11bits | ||
| 616 | + adts_header[5] |= 0x1f; | ||
| 617 | + | ||
| 618 | + // copy to audio buffer | ||
| 619 | + ab->append((const char*)adts_header, sizeof(adts_header)); | ||
| 620 | + ab->append(sample_unit->bytes, sample_unit->size); | ||
| 621 | + } | ||
| 622 | + | ||
| 623 | + return ret; | ||
| 624 | +} | ||
| 625 | + | ||
| 626 | +int SrsTsCache::do_cache_video(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
| 627 | +{ | ||
| 628 | + int ret = ERROR_SUCCESS; | ||
| 629 | + | ||
| 630 | + // for type1/5/6, insert aud packet. | ||
| 631 | + static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; | ||
| 632 | + | ||
| 633 | + bool sps_pps_sent = false; | ||
| 634 | + bool aud_sent = false; | ||
| 635 | + /** | ||
| 636 | + * a ts sample is format as: | ||
| 637 | + * 00 00 00 01 // header | ||
| 638 | + * xxxxxxx // data bytes | ||
| 639 | + * 00 00 01 // continue header | ||
| 640 | + * xxxxxxx // data bytes. | ||
| 641 | + * so, for each sample, we append header in aud_nal, then appends the bytes in sample. | ||
| 642 | + */ | ||
| 643 | + for (int i = 0; i < sample->nb_sample_units; i++) { | ||
| 644 | + SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; | ||
| 645 | + int32_t size = sample_unit->size; | ||
| 646 | + | ||
| 647 | + if (!sample_unit->bytes || size <= 0) { | ||
| 648 | + ret = ERROR_HLS_AVC_SAMPLE_SIZE; | ||
| 649 | + srs_error("invalid avc sample length=%d, ret=%d", size, ret); | ||
| 650 | + return ret; | ||
| 651 | + } | ||
| 652 | + | ||
| 653 | + /** | ||
| 654 | + * step 1: | ||
| 655 | + * first, before each "real" sample, | ||
| 656 | + * we add some packets according to the nal_unit_type, | ||
| 657 | + * for example, when got nal_unit_type=5, insert SPS/PPS before sample. | ||
| 658 | + */ | ||
| 659 | + | ||
| 660 | + // 5bits, 7.3.1 NAL unit syntax, | ||
| 661 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 662 | + u_int8_t nal_unit_type; | ||
| 663 | + nal_unit_type = *sample_unit->bytes; | ||
| 664 | + nal_unit_type &= 0x1f; | ||
| 665 | + | ||
| 666 | + // @see: ngx_rtmp_hls_video | ||
| 667 | + // Table 7-1 C NAL unit type codes, page 61 | ||
| 668 | + // 1: Coded slice | ||
| 669 | + if (nal_unit_type == 1) { | ||
| 670 | + sps_pps_sent = false; | ||
| 671 | + } | ||
| 672 | + | ||
| 673 | + // 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61 | ||
| 674 | + // @see: ngx_rtmp_hls_append_aud | ||
| 675 | + if (!aud_sent) { | ||
| 676 | + // @remark, when got type 9, we donot send aud_nal, but it will make | ||
| 677 | + // ios unhappy, so we remove it. | ||
| 678 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/281 | ||
| 679 | + /*if (nal_unit_type == 9) { | ||
| 680 | + aud_sent = true; | ||
| 681 | + }*/ | ||
| 682 | + | ||
| 683 | + if (nal_unit_type == 1 || nal_unit_type == 5 || nal_unit_type == 6) { | ||
| 684 | + // for type 6, append a aud with type 9. | ||
| 685 | + vb->append((const char*)aud_nal, sizeof(aud_nal)); | ||
| 686 | + aud_sent = true; | ||
| 687 | + } | ||
| 688 | + } | ||
| 689 | + | ||
| 690 | + // 5: Coded slice of an IDR picture. | ||
| 691 | + // insert sps/pps before IDR or key frame is ok. | ||
| 692 | + if (nal_unit_type == 5 && !sps_pps_sent) { | ||
| 693 | + sps_pps_sent = true; | ||
| 694 | + | ||
| 695 | + // @see: ngx_rtmp_hls_append_sps_pps | ||
| 696 | + if (codec->sequenceParameterSetLength > 0) { | ||
| 697 | + // AnnexB prefix, for sps always 4 bytes header | ||
| 698 | + vb->append((const char*)aud_nal, 4); | ||
| 699 | + // sps | ||
| 700 | + vb->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); | ||
| 701 | + } | ||
| 702 | + if (codec->pictureParameterSetLength > 0) { | ||
| 703 | + // AnnexB prefix, for pps always 4 bytes header | ||
| 704 | + vb->append((const char*)aud_nal, 4); | ||
| 705 | + // pps | ||
| 706 | + vb->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); | ||
| 707 | + } | ||
| 708 | + } | ||
| 709 | + | ||
| 710 | + // 7-9, ignore, @see: ngx_rtmp_hls_video | ||
| 711 | + if (nal_unit_type >= 7 && nal_unit_type <= 9) { | ||
| 712 | + continue; | ||
| 713 | + } | ||
| 714 | + | ||
| 715 | + /** | ||
| 716 | + * step 2: | ||
| 717 | + * output the "real" sample, in buf. | ||
| 718 | + * when we output some special assist packets according to nal_unit_type | ||
| 719 | + */ | ||
| 720 | + | ||
| 721 | + // sample start prefix, '00 00 00 01' or '00 00 01' | ||
| 722 | + u_int8_t* p = aud_nal + 1; | ||
| 723 | + u_int8_t* end = p + 3; | ||
| 724 | + | ||
| 725 | + // first AnnexB prefix is long (4 bytes) | ||
| 726 | + if (vb->length() == 0) { | ||
| 727 | + p = aud_nal; | ||
| 728 | + } | ||
| 729 | + vb->append((const char*)p, end - p); | ||
| 730 | + | ||
| 731 | + // sample data | ||
| 732 | + vb->append(sample_unit->bytes, sample_unit->size); | ||
| 733 | + } | ||
| 734 | + | ||
| 735 | + return ret; | ||
| 736 | +} | ||
| 40 | 737 | ||
| 41 | SrsTsEncoder::SrsTsEncoder() | 738 | SrsTsEncoder::SrsTsEncoder() |
| 42 | { | 739 | { |
| @@ -37,6 +37,110 @@ class SrsFileWriter; | @@ -37,6 +37,110 @@ class SrsFileWriter; | ||
| 37 | class SrsFileReader; | 37 | class SrsFileReader; |
| 38 | class SrsAvcAacCodec; | 38 | class SrsAvcAacCodec; |
| 39 | class SrsCodecSample; | 39 | class SrsCodecSample; |
| 40 | +class SrsSimpleBuffer; | ||
| 41 | + | ||
| 42 | +// @see: ngx_rtmp_SrsMpegtsFrame_t | ||
| 43 | +class SrsMpegtsFrame | ||
| 44 | +{ | ||
| 45 | +public: | ||
| 46 | + int64_t pts; | ||
| 47 | + int64_t dts; | ||
| 48 | + int pid; | ||
| 49 | + int sid; | ||
| 50 | + int cc; | ||
| 51 | + bool key; | ||
| 52 | + | ||
| 53 | + SrsMpegtsFrame(); | ||
| 54 | +}; | ||
| 55 | + | ||
| 56 | +/** | ||
| 57 | +* write data from frame(header info) and buffer(data) to ts file. | ||
| 58 | +* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter | ||
| 59 | +*/ | ||
| 60 | +class SrsTSMuxer | ||
| 61 | +{ | ||
| 62 | +private: | ||
| 63 | + SrsFileWriter* writer; | ||
| 64 | + std::string path; | ||
| 65 | +public: | ||
| 66 | + SrsTSMuxer(SrsFileWriter* w); | ||
| 67 | + virtual ~SrsTSMuxer(); | ||
| 68 | +public: | ||
| 69 | + virtual int open(std::string _path); | ||
| 70 | + virtual int write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); | ||
| 71 | + virtual int write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); | ||
| 72 | + virtual void close(); | ||
| 73 | +}; | ||
| 74 | + | ||
| 75 | +/** | ||
| 76 | +* jitter correct for audio, | ||
| 77 | +* the sample rate 44100/32000 will lost precise, | ||
| 78 | +* when mp4/ts(tbn=90000) covert to flv/rtmp(1000), | ||
| 79 | +* so the Hls on ipad or iphone will corrupt, | ||
| 80 | +* @see nginx-rtmp: est_pts | ||
| 81 | +*/ | ||
| 82 | +class SrsTsAacJitter | ||
| 83 | +{ | ||
| 84 | +private: | ||
| 85 | + int64_t base_pts; | ||
| 86 | + int64_t nb_samples; | ||
| 87 | + int sync_ms; | ||
| 88 | +public: | ||
| 89 | + SrsTsAacJitter(); | ||
| 90 | + virtual ~SrsTsAacJitter(); | ||
| 91 | + /** | ||
| 92 | + * when buffer start, calc the "correct" pts for ts, | ||
| 93 | + * @param flv_pts, the flv pts calc from flv header timestamp, | ||
| 94 | + * @param sample_rate, the sample rate in format(flv/RTMP packet header). | ||
| 95 | + * @param aac_sample_rate, the sample rate in codec(sequence header). | ||
| 96 | + * @return the calc correct pts. | ||
| 97 | + */ | ||
| 98 | + virtual int64_t on_buffer_start(int64_t flv_pts, int sample_rate, int aac_sample_rate); | ||
| 99 | + /** | ||
| 100 | + * when buffer continue, muxer donot write to file, | ||
| 101 | + * the audio buffer continue grow and donot need a pts, | ||
| 102 | + * for the ts audio PES packet only has one pts at the first time. | ||
| 103 | + */ | ||
| 104 | + virtual void on_buffer_continue(); | ||
| 105 | +}; | ||
| 106 | + | ||
| 107 | +/** | ||
| 108 | +* ts stream cache, | ||
| 109 | +* use to cache ts stream. | ||
| 110 | +* | ||
| 111 | +* about the flv tbn problem: | ||
| 112 | +* flv tbn is 1/1000, ts tbn is 1/90000, | ||
| 113 | +* when timestamp convert to flv tbn, it will loose precise, | ||
| 114 | +* so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter, | ||
| 115 | +* we use a aac jitter to correct the audio pts. | ||
| 116 | +*/ | ||
| 117 | +class SrsTsCache | ||
| 118 | +{ | ||
| 119 | +public: | ||
| 120 | + // current frame and buffer | ||
| 121 | + SrsMpegtsFrame* af; | ||
| 122 | + SrsSimpleBuffer* ab; | ||
| 123 | + SrsMpegtsFrame* vf; | ||
| 124 | + SrsSimpleBuffer* vb; | ||
| 125 | +protected: | ||
| 126 | + // time jitter for aac | ||
| 127 | + SrsTsAacJitter* aac_jitter; | ||
| 128 | +public: | ||
| 129 | + SrsTsCache(); | ||
| 130 | + virtual ~SrsTsCache(); | ||
| 131 | +public: | ||
| 132 | + /** | ||
| 133 | + * write audio to cache | ||
| 134 | + */ | ||
| 135 | + virtual int cache_audio(SrsAvcAacCodec* codec, int64_t pts, SrsCodecSample* sample); | ||
| 136 | + /** | ||
| 137 | + * write video to muxer. | ||
| 138 | + */ | ||
| 139 | + virtual int cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample); | ||
| 140 | +private: | ||
| 141 | + virtual int do_cache_audio(SrsAvcAacCodec* codec, SrsCodecSample* sample); | ||
| 142 | + virtual int do_cache_video(SrsAvcAacCodec* codec, SrsCodecSample* sample); | ||
| 143 | +}; | ||
| 40 | 144 | ||
| 41 | /** | 145 | /** |
| 42 | * encode data to ts file. | 146 | * encode data to ts file. |
-
请 注册 或 登录 后发表评论