正在显示
7 个修改的文件
包含
419 行增加
和
375 行删除
| @@ -51,369 +51,23 @@ using namespace std; | @@ -51,369 +51,23 @@ using namespace std; | ||
| 51 | #include <srs_kernel_file.hpp> | 51 | #include <srs_kernel_file.hpp> |
| 52 | #include <srs_protocol_buffer.hpp> | 52 | #include <srs_protocol_buffer.hpp> |
| 53 | 53 | ||
| 54 | -// max PES packets size to flush the video. | ||
| 55 | -#define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 1024 * 1024 | ||
| 56 | - | ||
| 57 | // drop the segment when duration of ts too small. | 54 | // drop the segment when duration of ts too small. |
| 58 | #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 | 55 | #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 |
| 59 | 56 | ||
| 60 | -// @see: NGX_RTMP_HLS_DELAY, | ||
| 61 | -// 63000: 700ms, ts_tbn=90000 | ||
| 62 | -#define SRS_AUTO_HLS_DELAY 63000 | ||
| 63 | - | ||
| 64 | -// @see: ngx_rtmp_mpegts_header | ||
| 65 | -u_int8_t mpegts_header[] = { | ||
| 66 | - /* TS */ | ||
| 67 | - 0x47, 0x40, 0x00, 0x10, 0x00, | ||
| 68 | - /* PSI */ | ||
| 69 | - 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
| 70 | - /* PAT */ | ||
| 71 | - 0x00, 0x01, 0xf0, 0x01, | ||
| 72 | - /* CRC */ | ||
| 73 | - 0x2e, 0x70, 0x19, 0x05, | ||
| 74 | - /* stuffing 167 bytes */ | ||
| 75 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 76 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 77 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 78 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 79 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 80 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 81 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 82 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 83 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 84 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 85 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 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, | ||
| 92 | - | ||
| 93 | - /* TS */ | ||
| 94 | - 0x47, 0x50, 0x01, 0x10, 0x00, | ||
| 95 | - /* PSI */ | ||
| 96 | - 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, | ||
| 97 | - /* PMT */ | ||
| 98 | - 0xe1, 0x00, | ||
| 99 | - 0xf0, 0x00, | ||
| 100 | - // must generate header with/without video, @see: | ||
| 101 | - // https://github.com/winlinvip/simple-rtmp-server/issues/40 | ||
| 102 | - 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ | ||
| 103 | - 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ | ||
| 104 | - /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ | ||
| 105 | - /* CRC */ | ||
| 106 | - 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ | ||
| 107 | - /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ | ||
| 108 | - /* stuffing 157 bytes */ | ||
| 109 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 110 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 111 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 112 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 113 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 114 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 115 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 116 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 117 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 118 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 119 | - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 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 | ||
| 125 | -}; | ||
| 126 | - | ||
| 127 | -// @see: ngx_rtmp_mpegts.c | ||
| 128 | -// TODO: support full mpegts feature in future. | ||
| 129 | -class SrsMpegtsWriter | ||
| 130 | -{ | ||
| 131 | -public: | ||
| 132 | - static int write_header(SrsFileWriter* writer) | ||
| 133 | - { | ||
| 134 | - int ret = ERROR_SUCCESS; | ||
| 135 | - | ||
| 136 | - if ((ret = writer->write(mpegts_header, sizeof(mpegts_header), NULL)) != ERROR_SUCCESS) { | ||
| 137 | - ret = ERROR_HLS_WRITE_FAILED; | ||
| 138 | - srs_error("write ts file header failed. ret=%d", ret); | ||
| 139 | - return ret; | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - return ret; | ||
| 143 | - } | ||
| 144 | - static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer) | ||
| 145 | - { | ||
| 146 | - int ret = ERROR_SUCCESS; | ||
| 147 | - | ||
| 148 | - if (!buffer->bytes() || buffer->length() <= 0) { | ||
| 149 | - return ret; | ||
| 150 | - } | ||
| 151 | - | ||
| 152 | - char* last = buffer->bytes() + buffer->length(); | ||
| 153 | - char* pos = buffer->bytes(); | ||
| 154 | - | ||
| 155 | - bool first = true; | ||
| 156 | - while (pos < last) { | ||
| 157 | - static char packet[188]; | ||
| 158 | - char* p = packet; | ||
| 159 | - | ||
| 160 | - frame->cc++; | ||
| 161 | - | ||
| 162 | - // sync_byte; //8bits | ||
| 163 | - *p++ = 0x47; | ||
| 164 | - // pid; //13bits | ||
| 165 | - *p++ = (frame->pid >> 8) & 0x1f; | ||
| 166 | - // payload_unit_start_indicator; //1bit | ||
| 167 | - if (first) { | ||
| 168 | - p[-1] |= 0x40; | ||
| 169 | - } | ||
| 170 | - *p++ = frame->pid; | ||
| 171 | - | ||
| 172 | - // transport_scrambling_control; //2bits | ||
| 173 | - // adaption_field_control; //2bits, 0x01: PayloadOnly | ||
| 174 | - // continuity_counter; //4bits | ||
| 175 | - *p++ = 0x10 | (frame->cc & 0x0f); | ||
| 176 | - | ||
| 177 | - if (first) { | ||
| 178 | - first = false; | ||
| 179 | - if (frame->key) { | ||
| 180 | - p[-1] |= 0x20; // Both Adaption and Payload | ||
| 181 | - *p++ = 7; // size | ||
| 182 | - *p++ = 0x50; // random access + PCR | ||
| 183 | - p = write_pcr(p, frame->dts - SRS_AUTO_HLS_DELAY); | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - // PES header | ||
| 187 | - // packet_start_code_prefix; //24bits, '00 00 01' | ||
| 188 | - *p++ = 0x00; | ||
| 189 | - *p++ = 0x00; | ||
| 190 | - *p++ = 0x01; | ||
| 191 | - //8bits | ||
| 192 | - *p++ = frame->sid; | ||
| 193 | - | ||
| 194 | - // pts(33bits) need 5bytes. | ||
| 195 | - u_int8_t header_size = 5; | ||
| 196 | - u_int8_t flags = 0x80; // pts | ||
| 197 | - | ||
| 198 | - // dts(33bits) need 5bytes also | ||
| 199 | - if (frame->dts != frame->pts) { | ||
| 200 | - header_size += 5; | ||
| 201 | - flags |= 0x40; // dts | ||
| 202 | - } | ||
| 203 | - | ||
| 204 | - // 3bytes: flag fields from PES_packet_length to PES_header_data_length | ||
| 205 | - int pes_size = (last - pos) + header_size + 3; | ||
| 206 | - if (pes_size > 0xffff) { | ||
| 207 | - /** | ||
| 208 | - * when actual packet length > 0xffff(65535), | ||
| 209 | - * which exceed the max u_int16_t packet length, | ||
| 210 | - * use 0 packet length, the next unit start indicates the end of packet. | ||
| 211 | - */ | ||
| 212 | - pes_size = 0; | ||
| 213 | - } | ||
| 214 | - | ||
| 215 | - // PES_packet_length; //16bits | ||
| 216 | - *p++ = (pes_size >> 8); | ||
| 217 | - *p++ = pes_size; | ||
| 218 | - | ||
| 219 | - // PES_scrambling_control; //2bits, '10' | ||
| 220 | - // PES_priority; //1bit | ||
| 221 | - // data_alignment_indicator; //1bit | ||
| 222 | - // copyright; //1bit | ||
| 223 | - // original_or_copy; //1bit | ||
| 224 | - *p++ = 0x80; /* H222 */ | ||
| 225 | - | ||
| 226 | - // PTS_DTS_flags; //2bits | ||
| 227 | - // ESCR_flag; //1bit | ||
| 228 | - // ES_rate_flag; //1bit | ||
| 229 | - // DSM_trick_mode_flag; //1bit | ||
| 230 | - // additional_copy_info_flag; //1bit | ||
| 231 | - // PES_CRC_flag; //1bit | ||
| 232 | - // PES_extension_flag; //1bit | ||
| 233 | - *p++ = flags; | ||
| 234 | - | ||
| 235 | - // PES_header_data_length; //8bits | ||
| 236 | - *p++ = header_size; | ||
| 237 | - | ||
| 238 | - // pts; // 33bits | ||
| 239 | - p = write_pts(p, flags >> 6, frame->pts + SRS_AUTO_HLS_DELAY); | ||
| 240 | - | ||
| 241 | - // dts; // 33bits | ||
| 242 | - if (frame->dts != frame->pts) { | ||
| 243 | - p = write_pts(p, 1, frame->dts + SRS_AUTO_HLS_DELAY); | ||
| 244 | - } | ||
| 245 | - } | ||
| 246 | - | ||
| 247 | - int body_size = sizeof(packet) - (p - packet); | ||
| 248 | - int in_size = last - pos; | ||
| 249 | - | ||
| 250 | - if (body_size <= in_size) { | ||
| 251 | - memcpy(p, pos, body_size); | ||
| 252 | - pos += body_size; | ||
| 253 | - } else { | ||
| 254 | - p = fill_stuff(p, packet, body_size, in_size); | ||
| 255 | - memcpy(p, pos, in_size); | ||
| 256 | - pos = last; | ||
| 257 | - } | ||
| 258 | - | ||
| 259 | - // write ts packet | ||
| 260 | - if ((ret = writer->write(packet, sizeof(packet), NULL)) != ERROR_SUCCESS) { | ||
| 261 | - ret = ERROR_HLS_WRITE_FAILED; | ||
| 262 | - srs_error("write ts file failed. ret=%d", ret); | ||
| 263 | - return ret; | ||
| 264 | - } | ||
| 265 | - } | ||
| 266 | - | ||
| 267 | - return ret; | ||
| 268 | - } | ||
| 269 | -private: | ||
| 270 | - static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) | ||
| 271 | - { | ||
| 272 | - char* p = pes_body_end; | ||
| 273 | - | ||
| 274 | - // insert the stuff bytes before PES body | ||
| 275 | - int stuff_size = (body_size - in_size); | ||
| 276 | - | ||
| 277 | - // adaption_field_control; //2bits | ||
| 278 | - if (packet[3] & 0x20) { | ||
| 279 | - // has adaptation | ||
| 280 | - // packet[4]: adaption_field_length | ||
| 281 | - // packet[5]: adaption field data | ||
| 282 | - // base: start of PES body | ||
| 283 | - char* base = &packet[5] + packet[4]; | ||
| 284 | - int len = p - base; | ||
| 285 | - p = (char*)memmove(base + stuff_size, base, len) + len; | ||
| 286 | - // increase the adaption field size. | ||
| 287 | - packet[4] += stuff_size; | ||
| 288 | - | ||
| 289 | - return p; | ||
| 290 | - } | ||
| 291 | - | ||
| 292 | - // create adaption field. | ||
| 293 | - // adaption_field_control; //2bits | ||
| 294 | - packet[3] |= 0x20; | ||
| 295 | - // base: start of PES body | ||
| 296 | - char* base = &packet[4]; | ||
| 297 | - int len = p - base; | ||
| 298 | - p = (char*)memmove(base + stuff_size, base, len) + len; | ||
| 299 | - // adaption_field_length; //8bits | ||
| 300 | - packet[4] = (stuff_size - 1); | ||
| 301 | - if (stuff_size >= 2) { | ||
| 302 | - // adaption field flags. | ||
| 303 | - packet[5] = 0; | ||
| 304 | - // adaption data. | ||
| 305 | - if (stuff_size > 2) { | ||
| 306 | - memset(&packet[6], 0xff, stuff_size - 2); | ||
| 307 | - } | ||
| 308 | - } | ||
| 309 | - | ||
| 310 | - return p; | ||
| 311 | - } | ||
| 312 | - static char* write_pcr(char* p, int64_t pcr) | ||
| 313 | - { | ||
| 314 | - // the pcr=dts-delay | ||
| 315 | - // and the pcr maybe negative | ||
| 316 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/268 | ||
| 317 | - int64_t v = srs_max(0, pcr); | ||
| 318 | - | ||
| 319 | - *p++ = (char) (v >> 25); | ||
| 320 | - *p++ = (char) (v >> 17); | ||
| 321 | - *p++ = (char) (v >> 9); | ||
| 322 | - *p++ = (char) (v >> 1); | ||
| 323 | - *p++ = (char) (v << 7 | 0x7e); | ||
| 324 | - *p++ = 0; | ||
| 325 | - | ||
| 326 | - return p; | ||
| 327 | - } | ||
| 328 | - static char* write_pts(char* p, u_int8_t fb, int64_t pts) | ||
| 329 | - { | ||
| 330 | - int32_t val; | ||
| 331 | - | ||
| 332 | - val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; | ||
| 333 | - *p++ = val; | ||
| 334 | - | ||
| 335 | - val = (((pts >> 15) & 0x7fff) << 1) | 1; | ||
| 336 | - *p++ = (val >> 8); | ||
| 337 | - *p++ = val; | ||
| 338 | - | ||
| 339 | - val = (((pts) & 0x7fff) << 1) | 1; | ||
| 340 | - *p++ = (val >> 8); | ||
| 341 | - *p++ = val; | ||
| 342 | - | ||
| 343 | - return p; | ||
| 344 | - } | ||
| 345 | -}; | ||
| 346 | - | ||
| 347 | -SrsTSMuxer::SrsTSMuxer() | ||
| 348 | -{ | ||
| 349 | - writer = new SrsFileWriter(); | ||
| 350 | -} | ||
| 351 | - | ||
| 352 | -SrsTSMuxer::~SrsTSMuxer() | ||
| 353 | -{ | ||
| 354 | - close(); | ||
| 355 | - srs_freep(writer); | ||
| 356 | -} | ||
| 357 | - | ||
| 358 | -int SrsTSMuxer::open(string _path) | ||
| 359 | -{ | ||
| 360 | - int ret = ERROR_SUCCESS; | ||
| 361 | - | ||
| 362 | - path = _path; | ||
| 363 | - | ||
| 364 | - close(); | ||
| 365 | - | ||
| 366 | - if ((ret = writer->open(path)) != ERROR_SUCCESS) { | ||
| 367 | - return ret; | ||
| 368 | - } | ||
| 369 | - | ||
| 370 | - // write mpegts header | ||
| 371 | - if ((ret = SrsMpegtsWriter::write_header(writer)) != ERROR_SUCCESS) { | ||
| 372 | - return ret; | ||
| 373 | - } | ||
| 374 | - | ||
| 375 | - return ret; | ||
| 376 | -} | ||
| 377 | - | ||
| 378 | -int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) | ||
| 379 | -{ | ||
| 380 | - int ret = ERROR_SUCCESS; | ||
| 381 | - | ||
| 382 | - if ((ret = SrsMpegtsWriter::write_frame(writer, af, ab)) != ERROR_SUCCESS) { | ||
| 383 | - return ret; | ||
| 384 | - } | ||
| 385 | - | ||
| 386 | - return ret; | ||
| 387 | -} | ||
| 388 | - | ||
| 389 | -int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) | ||
| 390 | -{ | ||
| 391 | - int ret = ERROR_SUCCESS; | ||
| 392 | - | ||
| 393 | - if ((ret = SrsMpegtsWriter::write_frame(writer, vf, vb)) != ERROR_SUCCESS) { | ||
| 394 | - return ret; | ||
| 395 | - } | ||
| 396 | - | ||
| 397 | - return ret; | ||
| 398 | -} | ||
| 399 | - | ||
| 400 | -void SrsTSMuxer::close() | ||
| 401 | -{ | ||
| 402 | - writer->close(); | ||
| 403 | -} | ||
| 404 | - | ||
| 405 | SrsHlsSegment::SrsHlsSegment() | 57 | SrsHlsSegment::SrsHlsSegment() |
| 406 | { | 58 | { |
| 407 | duration = 0; | 59 | duration = 0; |
| 408 | sequence_no = 0; | 60 | sequence_no = 0; |
| 409 | - muxer = new SrsTSMuxer(); | ||
| 410 | segment_start_dts = 0; | 61 | segment_start_dts = 0; |
| 411 | is_sequence_header = false; | 62 | is_sequence_header = false; |
| 63 | + writer = new SrsFileWriter(); | ||
| 64 | + muxer = new SrsTSMuxer(writer); | ||
| 412 | } | 65 | } |
| 413 | 66 | ||
| 414 | SrsHlsSegment::~SrsHlsSegment() | 67 | SrsHlsSegment::~SrsHlsSegment() |
| 415 | { | 68 | { |
| 416 | srs_freep(muxer); | 69 | srs_freep(muxer); |
| 70 | + srs_freep(writer); | ||
| 417 | } | 71 | } |
| 418 | 72 | ||
| 419 | void SrsHlsSegment::update_duration(int64_t current_frame_dts) | 73 | void SrsHlsSegment::update_duration(int64_t current_frame_dts) |
| @@ -926,6 +580,7 @@ int SrsHlsCache::write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t | @@ -926,6 +580,7 @@ int SrsHlsCache::write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t | ||
| 926 | return ret; | 580 | return ret; |
| 927 | } | 581 | } |
| 928 | } | 582 | } |
| 583 | + | ||
| 929 | // TODO: config it. | 584 | // TODO: config it. |
| 930 | // in ms, audio delay to flush the audios. | 585 | // in ms, audio delay to flush the audios. |
| 931 | int64_t audio_delay = SRS_CONF_DEFAULT_AAC_DELAY; | 586 | int64_t audio_delay = SRS_CONF_DEFAULT_AAC_DELAY; |
| @@ -53,25 +53,6 @@ class SrsTsAacJitter; | @@ -53,25 +53,6 @@ class SrsTsAacJitter; | ||
| 53 | class SrsTsCache; | 53 | class SrsTsCache; |
| 54 | 54 | ||
| 55 | /** | 55 | /** |
| 56 | -* write data from frame(header info) and buffer(data) to ts file. | ||
| 57 | -* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter | ||
| 58 | -*/ | ||
| 59 | -class SrsTSMuxer | ||
| 60 | -{ | ||
| 61 | -private: | ||
| 62 | - SrsFileWriter* writer; | ||
| 63 | - std::string path; | ||
| 64 | -public: | ||
| 65 | - SrsTSMuxer(); | ||
| 66 | - virtual ~SrsTSMuxer(); | ||
| 67 | -public: | ||
| 68 | - virtual int open(std::string _path); | ||
| 69 | - virtual int write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); | ||
| 70 | - virtual int write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); | ||
| 71 | - virtual void close(); | ||
| 72 | -}; | ||
| 73 | - | ||
| 74 | -/** | ||
| 75 | * the wrapper of m3u8 segment from specification: | 56 | * the wrapper of m3u8 segment from specification: |
| 76 | * | 57 | * |
| 77 | * 3.3.2. EXTINF | 58 | * 3.3.2. EXTINF |
| @@ -89,6 +70,7 @@ public: | @@ -89,6 +70,7 @@ public: | ||
| 89 | // ts full file to write. | 70 | // ts full file to write. |
| 90 | std::string full_path; | 71 | std::string full_path; |
| 91 | // the muxer to write ts. | 72 | // the muxer to write ts. |
| 73 | + SrsFileWriter* writer; | ||
| 92 | SrsTSMuxer* muxer; | 74 | SrsTSMuxer* muxer; |
| 93 | // current segment start dts for m3u8 | 75 | // current segment start dts for m3u8 |
| 94 | int64_t segment_start_dts; | 76 | int64_t segment_start_dts; |
| @@ -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 101 | 34 | +#define VERSION_REVISION 102 |
| 35 | 35 | ||
| 36 | // server info. | 36 | // server info. |
| 37 | #define RTMP_SIG_SRS_KEY "SRS" | 37 | #define RTMP_SIG_SRS_KEY "SRS" |
| @@ -28,6 +28,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -28,6 +28,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 28 | #include <srs_kernel_stream.hpp> | 28 | #include <srs_kernel_stream.hpp> |
| 29 | #include <srs_kernel_utility.hpp> | 29 | #include <srs_kernel_utility.hpp> |
| 30 | #include <srs_kernel_buffer.hpp> | 30 | #include <srs_kernel_buffer.hpp> |
| 31 | +#include <srs_kernel_file.hpp> | ||
| 32 | + | ||
| 33 | +using namespace std; | ||
| 31 | 34 | ||
| 32 | // in ms, for HLS aac sync time. | 35 | // in ms, for HLS aac sync time. |
| 33 | #define SRS_CONF_DEFAULT_AAC_SYNC 100 | 36 | #define SRS_CONF_DEFAULT_AAC_SYNC 100 |
| @@ -65,6 +68,294 @@ int aac_sample_rates[] = | @@ -65,6 +68,294 @@ int aac_sample_rates[] = | ||
| 65 | 7350, 0, 0, 0 | 68 | 7350, 0, 0, 0 |
| 66 | }; | 69 | }; |
| 67 | 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 | + | ||
| 68 | SrsMpegtsFrame::SrsMpegtsFrame() | 359 | SrsMpegtsFrame::SrsMpegtsFrame() |
| 69 | { | 360 | { |
| 70 | pts = dts = 0; | 361 | pts = dts = 0; |
| @@ -72,6 +363,63 @@ SrsMpegtsFrame::SrsMpegtsFrame() | @@ -72,6 +363,63 @@ SrsMpegtsFrame::SrsMpegtsFrame() | ||
| 72 | key = false; | 363 | key = false; |
| 73 | } | 364 | } |
| 74 | 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 | + | ||
| 75 | SrsTsAacJitter::SrsTsAacJitter() | 423 | SrsTsAacJitter::SrsTsAacJitter() |
| 76 | { | 424 | { |
| 77 | base_pts = 0; | 425 | base_pts = 0; |
| @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 30 | 30 | ||
| 31 | #include <srs_core.hpp> | 31 | #include <srs_core.hpp> |
| 32 | 32 | ||
| 33 | +#include <string> | ||
| 34 | + | ||
| 33 | #include <srs_kernel_codec.hpp> | 35 | #include <srs_kernel_codec.hpp> |
| 34 | 36 | ||
| 35 | class SrsStream; | 37 | class SrsStream; |
| @@ -37,6 +39,7 @@ class SrsMpegtsFrame; | @@ -37,6 +39,7 @@ class SrsMpegtsFrame; | ||
| 37 | class SrsSimpleBuffer; | 39 | class SrsSimpleBuffer; |
| 38 | class SrsAvcAacCodec; | 40 | class SrsAvcAacCodec; |
| 39 | class SrsCodecSample; | 41 | class SrsCodecSample; |
| 42 | +class SrsFileWriter; | ||
| 40 | 43 | ||
| 41 | /** | 44 | /** |
| 42 | * the public data, event HLS disable, others can use it. | 45 | * the public data, event HLS disable, others can use it. |
| @@ -57,6 +60,9 @@ extern int aac_sample_rates[]; | @@ -57,6 +60,9 @@ extern int aac_sample_rates[]; | ||
| 57 | // in ms, for HLS aac flush the audio | 60 | // in ms, for HLS aac flush the audio |
| 58 | #define SRS_CONF_DEFAULT_AAC_DELAY 100 | 61 | #define SRS_CONF_DEFAULT_AAC_DELAY 100 |
| 59 | 62 | ||
| 63 | +// max PES packets size to flush the video. | ||
| 64 | +#define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 1024 * 1024 | ||
| 65 | + | ||
| 60 | /** | 66 | /** |
| 61 | * the FLV/RTMP supported audio sample size. | 67 | * the FLV/RTMP supported audio sample size. |
| 62 | * Size of each audio sample. This parameter only pertains to | 68 | * Size of each audio sample. This parameter only pertains to |
| @@ -104,6 +110,25 @@ public: | @@ -104,6 +110,25 @@ public: | ||
| 104 | }; | 110 | }; |
| 105 | 111 | ||
| 106 | /** | 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 | +/** | ||
| 107 | * jitter correct for audio, | 132 | * jitter correct for audio, |
| 108 | * the sample rate 44100/32000 will lost precise, | 133 | * the sample rate 44100/32000 will lost precise, |
| 109 | * when mp4/ts(tbn=90000) covert to flv/rtmp(1000), | 134 | * when mp4/ts(tbn=90000) covert to flv/rtmp(1000), |
| @@ -36,18 +36,23 @@ using namespace std; | @@ -36,18 +36,23 @@ using namespace std; | ||
| 36 | #include <srs_kernel_error.hpp> | 36 | #include <srs_kernel_error.hpp> |
| 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 | 40 | ||
| 40 | SrsTsEncoder::SrsTsEncoder() | 41 | SrsTsEncoder::SrsTsEncoder() |
| 41 | { | 42 | { |
| 42 | _fs = NULL; | 43 | _fs = NULL; |
| 43 | codec = new SrsAvcAacCodec(); | 44 | codec = new SrsAvcAacCodec(); |
| 44 | sample = new SrsCodecSample(); | 45 | sample = new SrsCodecSample(); |
| 46 | + cache = new SrsTsCache(); | ||
| 47 | + muxer = NULL; | ||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | SrsTsEncoder::~SrsTsEncoder() | 50 | SrsTsEncoder::~SrsTsEncoder() |
| 48 | { | 51 | { |
| 49 | srs_freep(codec); | 52 | srs_freep(codec); |
| 50 | srs_freep(sample); | 53 | srs_freep(sample); |
| 54 | + srs_freep(cache); | ||
| 55 | + srs_freep(muxer); | ||
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | int SrsTsEncoder::initialize(SrsFileWriter* fs) | 58 | int SrsTsEncoder::initialize(SrsFileWriter* fs) |
| @@ -64,6 +69,13 @@ int SrsTsEncoder::initialize(SrsFileWriter* fs) | @@ -64,6 +69,13 @@ int SrsTsEncoder::initialize(SrsFileWriter* fs) | ||
| 64 | 69 | ||
| 65 | _fs = fs; | 70 | _fs = fs; |
| 66 | 71 | ||
| 72 | + srs_freep(muxer); | ||
| 73 | + muxer = new SrsTSMuxer(fs); | ||
| 74 | + | ||
| 75 | + if ((ret = muxer->open("")) != ERROR_SUCCESS) { | ||
| 76 | + return ret; | ||
| 77 | + } | ||
| 78 | + | ||
| 67 | return ret; | 79 | return ret; |
| 68 | } | 80 | } |
| 69 | 81 | ||
| @@ -91,10 +103,20 @@ int SrsTsEncoder::write_audio(int64_t timestamp, char* data, int size) | @@ -91,10 +103,20 @@ int SrsTsEncoder::write_audio(int64_t timestamp, char* data, int size) | ||
| 91 | // for the packet is filtered by consumer. | 103 | // for the packet is filtered by consumer. |
| 92 | int64_t dts = timestamp * 90; | 104 | int64_t dts = timestamp * 90; |
| 93 | 105 | ||
| 94 | - /*if ((ret = hls_cache->write_audio(codec, muxer, dts, sample)) != ERROR_SUCCESS) { | ||
| 95 | - srs_error("http: ts cache write audio failed. ret=%d", ret); | 106 | + // write audio to cache. |
| 107 | + if ((ret = cache->cache_audio(codec, dts, sample)) != ERROR_SUCCESS) { | ||
| 96 | return ret; | 108 | return ret; |
| 97 | - }*/ | 109 | + } |
| 110 | + | ||
| 111 | + // flush if buffer exceed max size. | ||
| 112 | + if (cache->ab->length() > SRS_AUTO_HLS_AUDIO_CACHE_SIZE) { | ||
| 113 | + if ((ret = muxer->write_audio(cache->af, cache->ab)) != ERROR_SUCCESS) { | ||
| 114 | + return ret; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + // write success, clear and free the buffer | ||
| 118 | + cache->ab->erase(cache->ab->length()); | ||
| 119 | + } | ||
| 98 | 120 | ||
| 99 | return ret; | 121 | return ret; |
| 100 | } | 122 | } |
| @@ -126,10 +148,18 @@ int SrsTsEncoder::write_video(int64_t timestamp, char* data, int size) | @@ -126,10 +148,18 @@ int SrsTsEncoder::write_video(int64_t timestamp, char* data, int size) | ||
| 126 | } | 148 | } |
| 127 | 149 | ||
| 128 | int64_t dts = timestamp * 90; | 150 | int64_t dts = timestamp * 90; |
| 129 | - /*if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) { | ||
| 130 | - srs_error("http: ts cache write video failed. ret=%d", ret); | 151 | + |
| 152 | + // write video to cache. | ||
| 153 | + if ((ret = cache->cache_video(codec, dts, sample)) != ERROR_SUCCESS) { | ||
| 154 | + return ret; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + if ((ret = muxer->write_video(cache->vf, cache->vb)) != ERROR_SUCCESS) { | ||
| 131 | return ret; | 158 | return ret; |
| 132 | - }*/ | 159 | + } |
| 160 | + | ||
| 161 | + // write success, clear and free the buffer | ||
| 162 | + cache->vb->erase(cache->vb->length()); | ||
| 133 | 163 | ||
| 134 | return ret; | 164 | return ret; |
| 135 | } | 165 | } |
| @@ -31,6 +31,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,6 +31,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | 31 | ||
| 32 | #include <string> | 32 | #include <string> |
| 33 | 33 | ||
| 34 | +class SrsTsCache; | ||
| 35 | +class SrsTSMuxer; | ||
| 34 | class SrsFileWriter; | 36 | class SrsFileWriter; |
| 35 | class SrsFileReader; | 37 | class SrsFileReader; |
| 36 | class SrsAvcAacCodec; | 38 | class SrsAvcAacCodec; |
| @@ -46,6 +48,8 @@ private: | @@ -46,6 +48,8 @@ private: | ||
| 46 | private: | 48 | private: |
| 47 | SrsAvcAacCodec* codec; | 49 | SrsAvcAacCodec* codec; |
| 48 | SrsCodecSample* sample; | 50 | SrsCodecSample* sample; |
| 51 | + SrsTsCache* cache; | ||
| 52 | + SrsTSMuxer* muxer; | ||
| 49 | public: | 53 | public: |
| 50 | SrsTsEncoder(); | 54 | SrsTsEncoder(); |
| 51 | virtual ~SrsTsEncoder(); | 55 | virtual ~SrsTsEncoder(); |
-
请 注册 或 登录 后发表评论