正在显示
2 个修改的文件
包含
531 行增加
和
442 行删除
| @@ -42,6 +42,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -42,6 +42,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 42 | #include <srs_core_autofree.hpp> | 42 | #include <srs_core_autofree.hpp> |
| 43 | #include <srs_core_rtmp.hpp> | 43 | #include <srs_core_rtmp.hpp> |
| 44 | 44 | ||
| 45 | +// max PES packets size to flush the video. | ||
| 46 | +#define SRS_HLS_AUDIO_CACHE_SIZE 512 * 1024 | ||
| 47 | + | ||
| 45 | // @see: NGX_RTMP_HLS_DELAY, | 48 | // @see: NGX_RTMP_HLS_DELAY, |
| 46 | // 63000: 700ms, ts_tbn=90000 | 49 | // 63000: 700ms, ts_tbn=90000 |
| 47 | #define SRS_HLS_DELAY 63000 | 50 | #define SRS_HLS_DELAY 63000 |
| @@ -399,40 +402,9 @@ void SrsHlsAacJitter::on_buffer_continue() | @@ -399,40 +402,9 @@ void SrsHlsAacJitter::on_buffer_continue() | ||
| 399 | nb_samples++; | 402 | nb_samples++; |
| 400 | } | 403 | } |
| 401 | 404 | ||
| 402 | -SrsM3u8Segment::SrsM3u8Segment() | ||
| 403 | -{ | ||
| 404 | - duration = 0; | ||
| 405 | - sequence_no = 0; | ||
| 406 | - muxer = new SrsTSMuxer(); | ||
| 407 | - segment_start_dts = 0; | ||
| 408 | -} | ||
| 409 | - | ||
| 410 | -SrsM3u8Segment::~SrsM3u8Segment() | ||
| 411 | -{ | ||
| 412 | - srs_freep(muxer); | ||
| 413 | -} | ||
| 414 | - | ||
| 415 | -double SrsM3u8Segment::update_duration(int64_t video_stream_dts) | ||
| 416 | -{ | ||
| 417 | - duration = (video_stream_dts - segment_start_dts) / 90000.0; | ||
| 418 | - srs_assert(duration > 0); | ||
| 419 | - | ||
| 420 | - return duration; | ||
| 421 | -} | ||
| 422 | - | ||
| 423 | -SrsHlsAacJitter::SrsHlsAacJitter() | ||
| 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 | SrsTSMuxer::SrsTSMuxer() | 405 | SrsTSMuxer::SrsTSMuxer() |
| 433 | { | 406 | { |
| 434 | fd = -1; | 407 | fd = -1; |
| 435 | - _fresh = false; | ||
| 436 | } | 408 | } |
| 437 | 409 | ||
| 438 | SrsTSMuxer::~SrsTSMuxer() | 410 | SrsTSMuxer::~SrsTSMuxer() |
| @@ -461,29 +433,25 @@ int SrsTSMuxer::open(std::string _path) | @@ -461,29 +433,25 @@ int SrsTSMuxer::open(std::string _path) | ||
| 461 | return ret; | 433 | return ret; |
| 462 | } | 434 | } |
| 463 | 435 | ||
| 464 | - _fresh = true; | ||
| 465 | - | ||
| 466 | return ret; | 436 | return ret; |
| 467 | } | 437 | } |
| 468 | 438 | ||
| 469 | -int SrsTSMuxer::write_audio(SrsMpegtsFrame* audio_frame, SrsCodecBuffer* audio_buffer) | 439 | +int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab) |
| 470 | { | 440 | { |
| 471 | int ret = ERROR_SUCCESS; | 441 | int ret = ERROR_SUCCESS; |
| 472 | 442 | ||
| 473 | - if ((ret = SrsMpegtsWriter::write_frame(fd, audio_frame, audio_buffer)) != ERROR_SUCCESS) { | 443 | + if ((ret = SrsMpegtsWriter::write_frame(fd, af, ab)) != ERROR_SUCCESS) { |
| 474 | return ret; | 444 | return ret; |
| 475 | } | 445 | } |
| 476 | 446 | ||
| 477 | - _fresh = false; | ||
| 478 | - | ||
| 479 | return ret; | 447 | return ret; |
| 480 | } | 448 | } |
| 481 | 449 | ||
| 482 | -int SrsTSMuxer::write_video(SrsMpegtsFrame* video_frame, SrsCodecBuffer* video_buffer) | 450 | +int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsCodecBuffer* vb) |
| 483 | { | 451 | { |
| 484 | int ret = ERROR_SUCCESS; | 452 | int ret = ERROR_SUCCESS; |
| 485 | 453 | ||
| 486 | - if ((ret = SrsMpegtsWriter::write_frame(fd, video_frame, video_buffer)) != ERROR_SUCCESS) { | 454 | + if ((ret = SrsMpegtsWriter::write_frame(fd, vf, vb)) != ERROR_SUCCESS) { |
| 487 | return ret; | 455 | return ret; |
| 488 | } | 456 | } |
| 489 | 457 | ||
| @@ -495,44 +463,49 @@ void SrsTSMuxer::close() | @@ -495,44 +463,49 @@ void SrsTSMuxer::close() | ||
| 495 | if (fd > 0) { | 463 | if (fd > 0) { |
| 496 | ::close(fd); | 464 | ::close(fd); |
| 497 | fd = -1; | 465 | fd = -1; |
| 498 | - _fresh = false; | ||
| 499 | } | 466 | } |
| 500 | } | 467 | } |
| 501 | 468 | ||
| 502 | -bool SrsTSMuxer::fresh() | 469 | +SrsM3u8Segment::SrsM3u8Segment() |
| 503 | { | 470 | { |
| 504 | - return _fresh; | 471 | + duration = 0; |
| 472 | + sequence_no = 0; | ||
| 473 | + muxer = new SrsTSMuxer(); | ||
| 474 | + segment_start_dts = 0; | ||
| 505 | } | 475 | } |
| 506 | 476 | ||
| 507 | -SrsHls::SrsHls() | 477 | +SrsM3u8Segment::~SrsM3u8Segment() |
| 508 | { | 478 | { |
| 509 | - hls_enabled = false; | ||
| 510 | - codec = new SrsCodec(); | ||
| 511 | - sample = new SrsCodecSample(); | ||
| 512 | - current = NULL; | ||
| 513 | - jitter = new SrsRtmpJitter(); | ||
| 514 | - aac_jitter = new SrsHlsAacJitter(); | ||
| 515 | - file_index = 0; | ||
| 516 | - audio_buffer_start_pts = video_stream_dts = 0; | ||
| 517 | - hls_fragment = hls_window = 0; | 479 | + srs_freep(muxer); |
| 480 | +} | ||
| 518 | 481 | ||
| 519 | - // TODO: config it. | ||
| 520 | - audio_delay = SRS_CONF_DEFAULT_AAC_DELAY; | ||
| 521 | - | ||
| 522 | - audio_buffer = new SrsCodecBuffer(); | ||
| 523 | - video_buffer = new SrsCodecBuffer(); | 482 | +double SrsM3u8Segment::update_duration(int64_t video_stream_dts) |
| 483 | +{ | ||
| 484 | + duration = (video_stream_dts - segment_start_dts) / 90000.0; | ||
| 485 | + srs_assert(duration >= 0); | ||
| 524 | 486 | ||
| 525 | - audio_frame = new SrsMpegtsFrame(); | ||
| 526 | - video_frame = new SrsMpegtsFrame(); | 487 | + return duration; |
| 527 | } | 488 | } |
| 528 | 489 | ||
| 529 | -SrsHls::~SrsHls() | 490 | +SrsHlsAacJitter::SrsHlsAacJitter() |
| 530 | { | 491 | { |
| 531 | - srs_freep(codec); | ||
| 532 | - srs_freep(sample); | ||
| 533 | - srs_freep(jitter); | ||
| 534 | - srs_freep(aac_jitter); | 492 | + base_pts = 0; |
| 493 | + nb_samples = 0; | ||
| 494 | + | ||
| 495 | + // TODO: config it, 0 means no adjust | ||
| 496 | + sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; | ||
| 497 | +} | ||
| 498 | + | ||
| 499 | +SrsM3u8Muxer::SrsM3u8Muxer() | ||
| 500 | +{ | ||
| 501 | + hls_fragment = hls_window = 0; | ||
| 502 | + video_stream_dts = 0; | ||
| 503 | + file_index = 0; | ||
| 504 | + current = NULL; | ||
| 505 | +} | ||
| 535 | 506 | ||
| 507 | +SrsM3u8Muxer::~SrsM3u8Muxer() | ||
| 508 | +{ | ||
| 536 | std::vector<SrsM3u8Segment*>::iterator it; | 509 | std::vector<SrsM3u8Segment*>::iterator it; |
| 537 | for (it = segments.begin(); it != segments.end(); ++it) { | 510 | for (it = segments.begin(); it != segments.end(); ++it) { |
| 538 | SrsM3u8Segment* segment = *it; | 511 | SrsM3u8Segment* segment = *it; |
| @@ -541,373 +514,217 @@ SrsHls::~SrsHls() | @@ -541,373 +514,217 @@ SrsHls::~SrsHls() | ||
| 541 | segments.clear(); | 514 | segments.clear(); |
| 542 | 515 | ||
| 543 | srs_freep(current); | 516 | srs_freep(current); |
| 517 | +} | ||
| 518 | + | ||
| 519 | +int SrsM3u8Muxer::update_config( | ||
| 520 | + std::string _app, std::string _stream, | ||
| 521 | + std::string path, int fragment, int window | ||
| 522 | +) { | ||
| 523 | + int ret = ERROR_SUCCESS; | ||
| 544 | 524 | ||
| 545 | - audio_buffer->free(); | ||
| 546 | - video_buffer->free(); | ||
| 547 | - | ||
| 548 | - srs_freep(audio_buffer); | ||
| 549 | - srs_freep(video_buffer); | 525 | + app = _app; |
| 526 | + stream = _stream; | ||
| 527 | + hls_path = path; | ||
| 528 | + hls_fragment = fragment; | ||
| 529 | + hls_window = window; | ||
| 550 | 530 | ||
| 551 | - srs_freep(audio_frame); | ||
| 552 | - srs_freep(video_frame); | 531 | + return ret; |
| 553 | } | 532 | } |
| 554 | 533 | ||
| 555 | -int SrsHls::on_publish(SrsRequest* req) | 534 | +int SrsM3u8Muxer::segment_open() |
| 556 | { | 535 | { |
| 557 | int ret = ERROR_SUCCESS; | 536 | int ret = ERROR_SUCCESS; |
| 558 | - | ||
| 559 | - vhost = req->vhost; | ||
| 560 | - stream = req->stream; | ||
| 561 | - app = req->app; | ||
| 562 | - | ||
| 563 | - // TODO: subscribe the reload event. | ||
| 564 | 537 | ||
| 565 | - SrsConfDirective* conf = NULL; | ||
| 566 | - if ((conf = config->get_hls_fragment(vhost)) != NULL && !conf->arg0().empty()) { | ||
| 567 | - hls_fragment = ::atoi(conf->arg0().c_str()); | ||
| 568 | - } | ||
| 569 | - if (hls_fragment <= 0) { | ||
| 570 | - hls_fragment = SRS_CONF_DEFAULT_HLS_FRAGMENT; | 538 | + // TODO: create all parents dirs. |
| 539 | + // create dir for app. | ||
| 540 | + if ((ret = create_dir()) != ERROR_SUCCESS) { | ||
| 541 | + return ret; | ||
| 571 | } | 542 | } |
| 572 | 543 | ||
| 573 | - if ((conf = config->get_hls_window(vhost)) != NULL && !conf->arg0().empty()) { | ||
| 574 | - hls_window = ::atoi(conf->arg0().c_str()); | ||
| 575 | - } | ||
| 576 | - if (hls_window <= 0) { | ||
| 577 | - hls_window = SRS_CONF_DEFAULT_HLS_WINDOW; | ||
| 578 | - } | 544 | + // when segment open, the current segment must be NULL. |
| 545 | + srs_assert(!current); | ||
| 546 | + | ||
| 547 | + // new segment. | ||
| 548 | + current = new SrsM3u8Segment(); | ||
| 549 | + current->sequence_no = file_index++; | ||
| 550 | + current->segment_start_dts = video_stream_dts; | ||
| 551 | + | ||
| 552 | + // generate filename. | ||
| 553 | + char filename[128]; | ||
| 554 | + snprintf(filename, sizeof(filename), | ||
| 555 | + "%s-%d.ts", stream.c_str(), current->sequence_no); | ||
| 556 | + | ||
| 557 | + // TODO: use temp file and rename it. | ||
| 558 | + current->full_path = hls_path; | ||
| 559 | + current->full_path += "/"; | ||
| 560 | + current->full_path += app; | ||
| 561 | + current->full_path += "/"; | ||
| 562 | + current->full_path += filename; | ||
| 579 | 563 | ||
| 580 | - if ((ret = reopen()) != ERROR_SUCCESS) { | 564 | + // TODO: support base url, and so on. |
| 565 | + current->uri = filename; | ||
| 566 | + | ||
| 567 | + if ((ret = current->muxer->open(current->full_path)) != ERROR_SUCCESS) { | ||
| 568 | + srs_error("open hls muxer failed. ret=%d", ret); | ||
| 581 | return ret; | 569 | return ret; |
| 582 | } | 570 | } |
| 583 | - | 571 | + srs_info("open HLS muxer success. vhost=%s, path=%s", |
| 572 | + vhost.c_str(), current->full_path.c_str()); | ||
| 573 | + | ||
| 584 | return ret; | 574 | return ret; |
| 585 | } | 575 | } |
| 586 | 576 | ||
| 587 | -void SrsHls::on_unpublish() | ||
| 588 | -{ | ||
| 589 | - hls_enabled = false; | ||
| 590 | -} | ||
| 591 | - | ||
| 592 | -int SrsHls::on_meta_data(SrsOnMetaDataPacket* metadata) | 577 | +int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab) |
| 593 | { | 578 | { |
| 594 | int ret = ERROR_SUCCESS; | 579 | int ret = ERROR_SUCCESS; |
| 595 | 580 | ||
| 596 | - if (!metadata || !metadata->metadata) { | ||
| 597 | - srs_trace("no metadata persent, hls ignored it."); | 581 | + srs_assert(current); |
| 582 | + | ||
| 583 | + if (ab->size <= 0) { | ||
| 598 | return ret; | 584 | return ret; |
| 599 | } | 585 | } |
| 600 | 586 | ||
| 601 | - SrsAmf0Object* obj = metadata->metadata; | ||
| 602 | - if (obj->size() <= 0) { | ||
| 603 | - srs_trace("no metadata persent, hls ignored it."); | 587 | + if ((ret = current->muxer->write_audio(af, ab)) != ERROR_SUCCESS) { |
| 604 | return ret; | 588 | return ret; |
| 605 | } | 589 | } |
| 606 | 590 | ||
| 607 | - // finger out the codec info from metadata if possible. | ||
| 608 | - SrsAmf0Any* prop = NULL; | 591 | + // write success, clear and free the buffer |
| 592 | + ab->free(); | ||
| 609 | 593 | ||
| 610 | - if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { | ||
| 611 | - codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 612 | - } | ||
| 613 | - if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { | ||
| 614 | - codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 615 | - } | ||
| 616 | - if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { | ||
| 617 | - codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 618 | - } | ||
| 619 | - if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { | ||
| 620 | - codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 621 | - } | ||
| 622 | - if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { | ||
| 623 | - codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 624 | - } | ||
| 625 | - if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { | ||
| 626 | - codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
| 627 | - } | ||
| 628 | - | ||
| 629 | - if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { | ||
| 630 | - codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 631 | - } | ||
| 632 | - if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { | ||
| 633 | - codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
| 634 | - } | ||
| 635 | - | ||
| 636 | - // ignore the following, for each flv/rtmp packet contains them: | ||
| 637 | - // audiosamplerate, sample->sound_rate | ||
| 638 | - // audiosamplesize, sample->sound_size | ||
| 639 | - // stereo, sample->sound_type | ||
| 640 | - | ||
| 641 | return ret; | 594 | return ret; |
| 642 | } | 595 | } |
| 643 | 596 | ||
| 644 | -int SrsHls::on_audio(SrsSharedPtrMessage* audio) | 597 | +int SrsM3u8Muxer::flush_video( |
| 598 | + SrsMpegtsFrame* af, SrsCodecBuffer* ab, | ||
| 599 | + SrsMpegtsFrame* vf, SrsCodecBuffer* vb) | ||
| 645 | { | 600 | { |
| 646 | int ret = ERROR_SUCCESS; | 601 | int ret = ERROR_SUCCESS; |
| 602 | + | ||
| 603 | + srs_assert(current); | ||
| 647 | 604 | ||
| 648 | - SrsAutoFree(SrsSharedPtrMessage, audio, false); | 605 | + video_stream_dts = vf->dts; |
| 649 | 606 | ||
| 650 | - // TODO: maybe donot need to demux the aac? | ||
| 651 | - if (!hls_enabled) { | ||
| 652 | - return ret; | ||
| 653 | - } | 607 | + // reopen the muxer for a gop |
| 608 | + if (vf->key && current->duration >= hls_fragment) { | ||
| 609 | + // TODO: flush audio before or after segment? | ||
| 610 | + /* | ||
| 611 | + if ((ret = flush_audio(af, ab)) != ERROR_SUCCESS) { | ||
| 612 | + srs_error("m3u8 muxer flush audio failed. ret=%d", ret); | ||
| 613 | + return ret; | ||
| 614 | + } | ||
| 615 | + */ | ||
| 616 | + | ||
| 617 | + if ((ret = segment_close()) != ERROR_SUCCESS) { | ||
| 618 | + srs_error("m3u8 muxer close segment failed. ret=%d", ret); | ||
| 619 | + return ret; | ||
| 620 | + } | ||
| 621 | + | ||
| 622 | + if ((ret = segment_open()) != ERROR_SUCCESS) { | ||
| 623 | + srs_error("m3u8 muxer open segment failed. ret=%d", ret); | ||
| 624 | + return ret; | ||
| 625 | + } | ||
| 654 | 626 | ||
| 655 | - sample->clear(); | ||
| 656 | - if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
| 657 | - return ret; | 627 | + // TODO: flush audio before or after segment? |
| 628 | + // segment open, flush the audio. | ||
| 629 | + // @see: ngx_rtmp_hls_open_fragment | ||
| 630 | + /* start fragment with audio to make iPhone happy */ | ||
| 631 | + if ((ret = flush_audio(af, ab)) != ERROR_SUCCESS) { | ||
| 632 | + srs_error("m3u8 muxer flush audio failed. ret=%d", ret); | ||
| 633 | + return ret; | ||
| 634 | + } | ||
| 658 | } | 635 | } |
| 659 | 636 | ||
| 660 | - if (codec->audio_codec_id != SrsCodecAudioAAC) { | ||
| 661 | - return ret; | ||
| 662 | - } | 637 | + srs_assert(current); |
| 638 | + // update the duration of segment. | ||
| 639 | + current->update_duration(video_stream_dts); | ||
| 663 | 640 | ||
| 664 | - // ignore sequence header | ||
| 665 | - if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | 641 | + if ((ret = current->muxer->write_video(vf, vb)) != ERROR_SUCCESS) { |
| 666 | return ret; | 642 | return ret; |
| 667 | } | 643 | } |
| 644 | + | ||
| 645 | + // write success, clear and free the buffer | ||
| 646 | + vb->free(); | ||
| 668 | 647 | ||
| 669 | - int64_t corrected_time = 0; | ||
| 670 | - if ((ret = jitter->correct(audio, 0, 0, &corrected_time)) != ERROR_SUCCESS) { | ||
| 671 | - return ret; | ||
| 672 | - } | 648 | + return ret; |
| 649 | +} | ||
| 650 | + | ||
| 651 | +int SrsM3u8Muxer::segment_close() | ||
| 652 | +{ | ||
| 653 | + int ret = ERROR_SUCCESS; | ||
| 673 | 654 | ||
| 655 | + // when close current segment, the current segment must not be NULL. | ||
| 674 | srs_assert(current); | 656 | srs_assert(current); |
| 657 | + | ||
| 658 | + // assert segment duplicate. | ||
| 659 | + std::vector<SrsM3u8Segment*>::iterator it; | ||
| 660 | + it = std::find(segments.begin(), segments.end(), current); | ||
| 661 | + srs_assert(it == segments.end()); | ||
| 662 | + | ||
| 663 | + // valid, add to segments. | ||
| 664 | + segments.push_back(current); | ||
| 675 | 665 | ||
| 676 | - // the pts calc from rtmp/flv header. | ||
| 677 | - int64_t pts = corrected_time * 90; | 666 | + srs_trace("reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", |
| 667 | + current->sequence_no, current->uri.c_str(), current->duration, | ||
| 668 | + current->segment_start_dts); | ||
| 678 | 669 | ||
| 679 | - // flush if audio delay exceed | ||
| 680 | - if (pts - audio_buffer_start_pts > audio_delay * 90) { | ||
| 681 | - if ((ret = flush_audio()) != ERROR_SUCCESS) { | ||
| 682 | - return ret; | ||
| 683 | - } | ||
| 684 | - } | 670 | + // close the muxer of finished segment. |
| 671 | + srs_freep(current->muxer); | ||
| 672 | + current = NULL; | ||
| 685 | 673 | ||
| 686 | - // start buffer, set the audio_frame | ||
| 687 | - if (audio_buffer->size == 0) { | ||
| 688 | - pts = aac_jitter->on_buffer_start(pts, sample->sound_rate); | 674 | + // the segments to remove |
| 675 | + std::vector<SrsM3u8Segment*> segment_to_remove; | ||
| 676 | + | ||
| 677 | + // shrink the segments. | ||
| 678 | + double duration = 0; | ||
| 679 | + int remove_index = -1; | ||
| 680 | + for (int i = segments.size() - 1; i >= 0; i--) { | ||
| 681 | + SrsM3u8Segment* segment = segments[i]; | ||
| 682 | + duration += segment->duration; | ||
| 689 | 683 | ||
| 690 | - audio_frame->dts = audio_frame->pts = audio_buffer_start_pts = pts; | ||
| 691 | - audio_frame->pid = TS_AUDIO_PID; | ||
| 692 | - audio_frame->sid = TS_AUDIO_AAC; | ||
| 693 | - } else { | ||
| 694 | - aac_jitter->on_buffer_continue(); | 684 | + if ((int)duration > hls_window) { |
| 685 | + remove_index = i; | ||
| 686 | + break; | ||
| 687 | + } | ||
| 688 | + } | ||
| 689 | + for (int i = 0; i < remove_index && !segments.empty(); i++) { | ||
| 690 | + SrsM3u8Segment* segment = *segments.begin(); | ||
| 691 | + segments.erase(segments.begin()); | ||
| 692 | + segment_to_remove.push_back(segment); | ||
| 695 | } | 693 | } |
| 696 | 694 | ||
| 697 | - // write audio to cache. | ||
| 698 | - if ((ret = write_audio()) != ERROR_SUCCESS) { | ||
| 699 | - return ret; | 695 | + // refresh the m3u8, donot contains the removed ts |
| 696 | + ret = refresh_m3u8(); | ||
| 697 | + | ||
| 698 | + // remove the ts file. | ||
| 699 | + for (int i = 0; i < (int)segment_to_remove.size(); i++) { | ||
| 700 | + SrsM3u8Segment* segment = segment_to_remove[i]; | ||
| 701 | + unlink(segment->full_path.c_str()); | ||
| 702 | + srs_freep(segment); | ||
| 700 | } | 703 | } |
| 704 | + segment_to_remove.clear(); | ||
| 701 | 705 | ||
| 702 | - // write cache to file. | ||
| 703 | - if (audio_buffer->size > 1024 * 1024) { | ||
| 704 | - if ((ret = flush_audio()) != ERROR_SUCCESS) { | ||
| 705 | - return ret; | ||
| 706 | - } | 706 | + // check ret of refresh m3u8 |
| 707 | + if (ret != ERROR_SUCCESS) { | ||
| 708 | + srs_error("refresh m3u8 failed. ret=%d", ret); | ||
| 709 | + return ret; | ||
| 707 | } | 710 | } |
| 708 | 711 | ||
| 709 | return ret; | 712 | return ret; |
| 710 | } | 713 | } |
| 711 | 714 | ||
| 712 | -int SrsHls::on_video(SrsSharedPtrMessage* video) | 715 | +int SrsM3u8Muxer::refresh_m3u8() |
| 713 | { | 716 | { |
| 714 | int ret = ERROR_SUCCESS; | 717 | int ret = ERROR_SUCCESS; |
| 715 | 718 | ||
| 716 | - SrsAutoFree(SrsSharedPtrMessage, video, false); | 719 | + std::string m3u8_file = hls_path; |
| 720 | + m3u8_file += "/"; | ||
| 721 | + m3u8_file += app; | ||
| 722 | + m3u8_file += "/"; | ||
| 723 | + m3u8_file += stream; | ||
| 724 | + m3u8_file += ".m3u8"; | ||
| 717 | 725 | ||
| 718 | - // TODO: maybe donot need to demux the avc? | ||
| 719 | - if (!hls_enabled) { | ||
| 720 | - return ret; | ||
| 721 | - } | ||
| 722 | - | ||
| 723 | - sample->clear(); | ||
| 724 | - if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { | ||
| 725 | - return ret; | ||
| 726 | - } | ||
| 727 | - | ||
| 728 | - if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
| 729 | - return ret; | ||
| 730 | - } | ||
| 731 | - | ||
| 732 | - // ignore sequence header | ||
| 733 | - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | ||
| 734 | - return ret; | ||
| 735 | - } | ||
| 736 | - | ||
| 737 | - int64_t corrected_time = 0; | ||
| 738 | - if ((ret = jitter->correct(video, 0, 0, &corrected_time)) != ERROR_SUCCESS) { | ||
| 739 | - return ret; | ||
| 740 | - } | ||
| 741 | - | ||
| 742 | - // write video to cache. | ||
| 743 | - if ((ret = write_video()) != ERROR_SUCCESS) { | ||
| 744 | - return ret; | ||
| 745 | - } | ||
| 746 | - | ||
| 747 | - video_stream_dts = video_frame->dts = corrected_time * 90; | ||
| 748 | - video_frame->pts = video_frame->dts + sample->cts * 90; | ||
| 749 | - video_frame->pid = TS_VIDEO_PID; | ||
| 750 | - video_frame->sid = TS_VIDEO_AVC; | ||
| 751 | - video_frame->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
| 752 | - | ||
| 753 | - // reopen the muxer for a gop | ||
| 754 | - srs_assert(current); | ||
| 755 | - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame) { | ||
| 756 | - if (current->duration >= hls_fragment) { | ||
| 757 | - if ((ret = reopen()) != ERROR_SUCCESS) { | ||
| 758 | - return ret; | ||
| 759 | - } | ||
| 760 | - } | ||
| 761 | - } | ||
| 762 | - | ||
| 763 | - srs_assert(current); | ||
| 764 | - // update the duration of segment. | ||
| 765 | - current->update_duration(video_stream_dts); | ||
| 766 | - | ||
| 767 | - if ((ret = current->muxer->write_video(video_frame, video_buffer)) != ERROR_SUCCESS) { | ||
| 768 | - return ret; | ||
| 769 | - } | ||
| 770 | - | ||
| 771 | - // write success, clear and free the buffer | ||
| 772 | - video_buffer->free(); | ||
| 773 | - | ||
| 774 | - return ret; | ||
| 775 | -} | ||
| 776 | - | ||
| 777 | -int SrsHls::reopen() | ||
| 778 | -{ | ||
| 779 | - int ret = ERROR_SUCCESS; | ||
| 780 | - | ||
| 781 | - // try to open the HLS muxer | ||
| 782 | - if (!config->get_hls_enabled(vhost)) { | ||
| 783 | - return ret; | ||
| 784 | - } | ||
| 785 | - | ||
| 786 | - // TODO: check the audio and video, ensure both exsists. | ||
| 787 | - // for use fixed mpegts header specifeid the audio and video pid. | ||
| 788 | - | ||
| 789 | - hls_enabled = true; | ||
| 790 | - | ||
| 791 | - SrsConfDirective* conf = NULL; | ||
| 792 | - hls_path = SRS_CONF_DEFAULT_HLS_PATH; | ||
| 793 | - if ((conf = config->get_hls_path(vhost)) != NULL) { | ||
| 794 | - hls_path = conf->arg0(); | ||
| 795 | - } | ||
| 796 | - | ||
| 797 | - // TODO: create all parents dirs. | ||
| 798 | - // create dir for app. | ||
| 799 | - if ((ret = create_dir()) != ERROR_SUCCESS) { | ||
| 800 | - return ret; | ||
| 801 | - } | ||
| 802 | - | ||
| 803 | - // close current segment and update the m3u8 file. | ||
| 804 | - if (current) { | ||
| 805 | - // assert segment duplicate. | ||
| 806 | - std::vector<SrsM3u8Segment*>::iterator it; | ||
| 807 | - it = std::find(segments.begin(), segments.end(), current); | ||
| 808 | - srs_assert(it == segments.end()); | ||
| 809 | - | ||
| 810 | - // valid, add to segments. | ||
| 811 | - segments.push_back(current); | ||
| 812 | - | ||
| 813 | - srs_trace("reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", | ||
| 814 | - current->sequence_no, current->uri.c_str(), current->duration, | ||
| 815 | - current->segment_start_dts); | ||
| 816 | - | ||
| 817 | - // close the muxer of finished segment. | ||
| 818 | - srs_freep(current->muxer); | ||
| 819 | - current = NULL; | ||
| 820 | - | ||
| 821 | - // the segments to remove | ||
| 822 | - std::vector<SrsM3u8Segment*> segment_to_remove; | ||
| 823 | - | ||
| 824 | - // shrink the segments. | ||
| 825 | - double duration = 0; | ||
| 826 | - int remove_index = -1; | ||
| 827 | - for (int i = segments.size() - 1; i >= 0; i--) { | ||
| 828 | - SrsM3u8Segment* segment = segments[i]; | ||
| 829 | - duration += segment->duration; | ||
| 830 | - | ||
| 831 | - if ((int)duration > hls_window) { | ||
| 832 | - remove_index = i; | ||
| 833 | - break; | ||
| 834 | - } | ||
| 835 | - } | ||
| 836 | - for (int i = 0; i < remove_index && !segments.empty(); i++) { | ||
| 837 | - SrsM3u8Segment* segment = *segments.begin(); | ||
| 838 | - segments.erase(segments.begin()); | ||
| 839 | - segment_to_remove.push_back(segment); | ||
| 840 | - } | ||
| 841 | - | ||
| 842 | - // refresh the m3u8, donot contains the removed ts | ||
| 843 | - ret = refresh_m3u8(); | ||
| 844 | - | ||
| 845 | - // remove the ts file. | ||
| 846 | - for (int i = 0; i < (int)segment_to_remove.size(); i++) { | ||
| 847 | - SrsM3u8Segment* segment = segment_to_remove[i]; | ||
| 848 | - unlink(segment->full_path.c_str()); | ||
| 849 | - srs_freep(segment); | ||
| 850 | - } | ||
| 851 | - segment_to_remove.clear(); | ||
| 852 | - | ||
| 853 | - // check ret of refresh m3u8 | ||
| 854 | - if (ret != ERROR_SUCCESS) { | ||
| 855 | - srs_error("refresh m3u8 failed. ret=%d", ret); | ||
| 856 | - return ret; | ||
| 857 | - } | ||
| 858 | - } | ||
| 859 | - // new segment. | ||
| 860 | - current = new SrsM3u8Segment(); | ||
| 861 | - current->sequence_no = file_index++; | ||
| 862 | - current->segment_start_dts = video_stream_dts; | ||
| 863 | - | ||
| 864 | - // generate filename. | ||
| 865 | - char filename[128]; | ||
| 866 | - snprintf(filename, sizeof(filename), | ||
| 867 | - "%s-%d.ts", stream.c_str(), current->sequence_no); | ||
| 868 | - | ||
| 869 | - // TODO: use temp file and rename it. | ||
| 870 | - current->full_path = hls_path; | ||
| 871 | - current->full_path += "/"; | ||
| 872 | - current->full_path += app; | ||
| 873 | - current->full_path += "/"; | ||
| 874 | - current->full_path += filename; | ||
| 875 | - | ||
| 876 | - // TODO: support base url, and so on. | ||
| 877 | - current->uri = filename; | ||
| 878 | - | ||
| 879 | - if ((ret = current->muxer->open(current->full_path)) != ERROR_SUCCESS) { | ||
| 880 | - srs_error("open hls muxer failed. ret=%d", ret); | ||
| 881 | - return ret; | ||
| 882 | - } | ||
| 883 | - srs_info("open HLS muxer success. vhost=%s, path=%s", | ||
| 884 | - vhost.c_str(), current->full_path.c_str()); | ||
| 885 | - | ||
| 886 | - // segment open, flush the audio. | ||
| 887 | - // @see: ngx_rtmp_hls_open_fragment | ||
| 888 | - /* start fragment with audio to make iPhone happy */ | ||
| 889 | - if (current->muxer->fresh()) { | ||
| 890 | - if ((ret = flush_audio()) != ERROR_SUCCESS) { | ||
| 891 | - return ret; | ||
| 892 | - } | ||
| 893 | - } | ||
| 894 | - | ||
| 895 | - return ret; | ||
| 896 | -} | ||
| 897 | - | ||
| 898 | -int SrsHls::refresh_m3u8() | ||
| 899 | -{ | ||
| 900 | - int ret = ERROR_SUCCESS; | ||
| 901 | - | ||
| 902 | - std::string m3u8_file = hls_path; | ||
| 903 | - m3u8_file += "/"; | ||
| 904 | - m3u8_file += app; | ||
| 905 | - m3u8_file += "/"; | ||
| 906 | - m3u8_file += stream; | ||
| 907 | - m3u8_file += ".m3u8"; | ||
| 908 | - | ||
| 909 | - m3u8 = m3u8_file; | ||
| 910 | - m3u8_file += ".temp"; | 726 | + m3u8 = m3u8_file; |
| 727 | + m3u8_file += ".temp"; | ||
| 911 | 728 | ||
| 912 | int fd = -1; | 729 | int fd = -1; |
| 913 | ret = _refresh_m3u8(fd, m3u8_file); | 730 | ret = _refresh_m3u8(fd, m3u8_file); |
| @@ -925,7 +742,7 @@ int SrsHls::refresh_m3u8() | @@ -925,7 +742,7 @@ int SrsHls::refresh_m3u8() | ||
| 925 | return ret; | 742 | return ret; |
| 926 | } | 743 | } |
| 927 | 744 | ||
| 928 | -int SrsHls::_refresh_m3u8(int& fd, std::string m3u8_file) | 745 | +int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file) |
| 929 | { | 746 | { |
| 930 | int ret = ERROR_SUCCESS; | 747 | int ret = ERROR_SUCCESS; |
| 931 | 748 | ||
| @@ -1016,7 +833,7 @@ int SrsHls::_refresh_m3u8(int& fd, std::string m3u8_file) | @@ -1016,7 +833,7 @@ int SrsHls::_refresh_m3u8(int& fd, std::string m3u8_file) | ||
| 1016 | return ret; | 833 | return ret; |
| 1017 | } | 834 | } |
| 1018 | 835 | ||
| 1019 | -int SrsHls::create_dir() | 836 | +int SrsM3u8Muxer::create_dir() |
| 1020 | { | 837 | { |
| 1021 | int ret = ERROR_SUCCESS; | 838 | int ret = ERROR_SUCCESS; |
| 1022 | 839 | ||
| @@ -1039,7 +856,284 @@ int SrsHls::create_dir() | @@ -1039,7 +856,284 @@ int SrsHls::create_dir() | ||
| 1039 | return ret; | 856 | return ret; |
| 1040 | } | 857 | } |
| 1041 | 858 | ||
| 1042 | -int SrsHls::write_audio() | 859 | +SrsHls::SrsHls() |
| 860 | +{ | ||
| 861 | + hls_enabled = false; | ||
| 862 | + | ||
| 863 | + codec = new SrsCodec(); | ||
| 864 | + sample = new SrsCodecSample(); | ||
| 865 | + jitter = new SrsRtmpJitter(); | ||
| 866 | + aac_jitter = new SrsHlsAacJitter(); | ||
| 867 | + | ||
| 868 | + ab = new SrsCodecBuffer(); | ||
| 869 | + vb = new SrsCodecBuffer(); | ||
| 870 | + | ||
| 871 | + af = new SrsMpegtsFrame(); | ||
| 872 | + vf = new SrsMpegtsFrame(); | ||
| 873 | + | ||
| 874 | + muxer = new SrsM3u8Muxer(); | ||
| 875 | +} | ||
| 876 | + | ||
| 877 | +SrsHls::~SrsHls() | ||
| 878 | +{ | ||
| 879 | + srs_freep(codec); | ||
| 880 | + srs_freep(sample); | ||
| 881 | + srs_freep(jitter); | ||
| 882 | + srs_freep(aac_jitter); | ||
| 883 | + | ||
| 884 | + ab->free(); | ||
| 885 | + vb->free(); | ||
| 886 | + | ||
| 887 | + srs_freep(ab); | ||
| 888 | + srs_freep(vb); | ||
| 889 | + | ||
| 890 | + srs_freep(af); | ||
| 891 | + srs_freep(vf); | ||
| 892 | + | ||
| 893 | + srs_freep(muxer); | ||
| 894 | +} | ||
| 895 | + | ||
| 896 | +int SrsHls::on_publish(SrsRequest* req) | ||
| 897 | +{ | ||
| 898 | + int ret = ERROR_SUCCESS; | ||
| 899 | + | ||
| 900 | + std::string vhost = req->vhost; | ||
| 901 | + std::string stream = req->stream; | ||
| 902 | + std::string app = req->app; | ||
| 903 | + | ||
| 904 | + // TODO: support reload. | ||
| 905 | + if (!config->get_hls_enabled(vhost)) { | ||
| 906 | + return ret; | ||
| 907 | + } | ||
| 908 | + | ||
| 909 | + // if enabled, open the muxer. | ||
| 910 | + hls_enabled = true; | ||
| 911 | + | ||
| 912 | + // TODO: subscribe the reload event. | ||
| 913 | + int hls_fragment = 0; | ||
| 914 | + int hls_window = 0; | ||
| 915 | + | ||
| 916 | + SrsConfDirective* conf = NULL; | ||
| 917 | + if ((conf = config->get_hls_fragment(vhost)) != NULL && !conf->arg0().empty()) { | ||
| 918 | + hls_fragment = ::atoi(conf->arg0().c_str()); | ||
| 919 | + } | ||
| 920 | + if (hls_fragment <= 0) { | ||
| 921 | + hls_fragment = SRS_CONF_DEFAULT_HLS_FRAGMENT; | ||
| 922 | + } | ||
| 923 | + | ||
| 924 | + if ((conf = config->get_hls_window(vhost)) != NULL && !conf->arg0().empty()) { | ||
| 925 | + hls_window = ::atoi(conf->arg0().c_str()); | ||
| 926 | + } | ||
| 927 | + if (hls_window <= 0) { | ||
| 928 | + hls_window = SRS_CONF_DEFAULT_HLS_WINDOW; | ||
| 929 | + } | ||
| 930 | + | ||
| 931 | + // get the hls path config | ||
| 932 | + std::string hls_path = SRS_CONF_DEFAULT_HLS_PATH; | ||
| 933 | + if ((conf = config->get_hls_path(vhost)) != NULL) { | ||
| 934 | + hls_path = conf->arg0(); | ||
| 935 | + } | ||
| 936 | + | ||
| 937 | + // open muxer | ||
| 938 | + if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) { | ||
| 939 | + srs_error("m3u8 muxer update config failed. ret=%d", ret); | ||
| 940 | + return ret; | ||
| 941 | + } | ||
| 942 | + | ||
| 943 | + if ((ret = muxer->segment_open()) != ERROR_SUCCESS) { | ||
| 944 | + srs_error("m3u8 muxer open segment failed. ret=%d", ret); | ||
| 945 | + return ret; | ||
| 946 | + } | ||
| 947 | + | ||
| 948 | + return ret; | ||
| 949 | +} | ||
| 950 | + | ||
| 951 | +void SrsHls::on_unpublish() | ||
| 952 | +{ | ||
| 953 | + int ret = ERROR_SUCCESS; | ||
| 954 | + | ||
| 955 | + // close muxer when unpublish. | ||
| 956 | + ret = muxer->flush_audio(af, ab); | ||
| 957 | + ret += muxer->segment_close(); | ||
| 958 | + if (ret != ERROR_SUCCESS) { | ||
| 959 | + srs_error("ignore m3u8 muxer flush/close audio failed. ret=%d", ret); | ||
| 960 | + return; | ||
| 961 | + } | ||
| 962 | + | ||
| 963 | + hls_enabled = false; | ||
| 964 | +} | ||
| 965 | + | ||
| 966 | +int SrsHls::on_meta_data(SrsOnMetaDataPacket* metadata) | ||
| 967 | +{ | ||
| 968 | + int ret = ERROR_SUCCESS; | ||
| 969 | + | ||
| 970 | + if (!metadata || !metadata->metadata) { | ||
| 971 | + srs_trace("no metadata persent, hls ignored it."); | ||
| 972 | + return ret; | ||
| 973 | + } | ||
| 974 | + | ||
| 975 | + SrsAmf0Object* obj = metadata->metadata; | ||
| 976 | + if (obj->size() <= 0) { | ||
| 977 | + srs_trace("no metadata persent, hls ignored it."); | ||
| 978 | + return ret; | ||
| 979 | + } | ||
| 980 | + | ||
| 981 | + // finger out the codec info from metadata if possible. | ||
| 982 | + SrsAmf0Any* prop = NULL; | ||
| 983 | + | ||
| 984 | + if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { | ||
| 985 | + codec->duration = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 986 | + } | ||
| 987 | + if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { | ||
| 988 | + codec->width = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 989 | + } | ||
| 990 | + if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { | ||
| 991 | + codec->height = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 992 | + } | ||
| 993 | + if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { | ||
| 994 | + codec->frame_rate = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 995 | + } | ||
| 996 | + if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { | ||
| 997 | + codec->video_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 998 | + } | ||
| 999 | + if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { | ||
| 1000 | + codec->video_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
| 1001 | + } | ||
| 1002 | + | ||
| 1003 | + if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { | ||
| 1004 | + codec->audio_codec_id = (int)srs_amf0_convert<SrsAmf0Number>(prop)->value; | ||
| 1005 | + } | ||
| 1006 | + if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { | ||
| 1007 | + codec->audio_data_rate = (int)(1000 * srs_amf0_convert<SrsAmf0Number>(prop)->value); | ||
| 1008 | + } | ||
| 1009 | + | ||
| 1010 | + // ignore the following, for each flv/rtmp packet contains them: | ||
| 1011 | + // audiosamplerate, sample->sound_rate | ||
| 1012 | + // audiosamplesize, sample->sound_size | ||
| 1013 | + // stereo, sample->sound_type | ||
| 1014 | + | ||
| 1015 | + return ret; | ||
| 1016 | +} | ||
| 1017 | + | ||
| 1018 | +int SrsHls::on_audio(SrsSharedPtrMessage* audio) | ||
| 1019 | +{ | ||
| 1020 | + int ret = ERROR_SUCCESS; | ||
| 1021 | + | ||
| 1022 | + SrsAutoFree(SrsSharedPtrMessage, audio, false); | ||
| 1023 | + | ||
| 1024 | + // TODO: maybe donot need to demux the aac? | ||
| 1025 | + if (!hls_enabled) { | ||
| 1026 | + return ret; | ||
| 1027 | + } | ||
| 1028 | + | ||
| 1029 | + sample->clear(); | ||
| 1030 | + if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
| 1031 | + return ret; | ||
| 1032 | + } | ||
| 1033 | + | ||
| 1034 | + if (codec->audio_codec_id != SrsCodecAudioAAC) { | ||
| 1035 | + return ret; | ||
| 1036 | + } | ||
| 1037 | + | ||
| 1038 | + // ignore sequence header | ||
| 1039 | + if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | ||
| 1040 | + return ret; | ||
| 1041 | + } | ||
| 1042 | + | ||
| 1043 | + int64_t corrected_time = 0; | ||
| 1044 | + if ((ret = jitter->correct(audio, 0, 0, &corrected_time)) != ERROR_SUCCESS) { | ||
| 1045 | + return ret; | ||
| 1046 | + } | ||
| 1047 | + | ||
| 1048 | + // the pts calc from rtmp/flv header. | ||
| 1049 | + int64_t pts = corrected_time * 90; | ||
| 1050 | + | ||
| 1051 | + // start buffer, set the af | ||
| 1052 | + if (ab->size == 0) { | ||
| 1053 | + pts = aac_jitter->on_buffer_start(pts, sample->sound_rate); | ||
| 1054 | + | ||
| 1055 | + af->dts = af->pts = audio_buffer_start_pts = pts; | ||
| 1056 | + af->pid = TS_AUDIO_PID; | ||
| 1057 | + af->sid = TS_AUDIO_AAC; | ||
| 1058 | + } else { | ||
| 1059 | + aac_jitter->on_buffer_continue(); | ||
| 1060 | + } | ||
| 1061 | + | ||
| 1062 | + // write audio to cache. | ||
| 1063 | + if ((ret = cache_audio()) != ERROR_SUCCESS) { | ||
| 1064 | + return ret; | ||
| 1065 | + } | ||
| 1066 | + | ||
| 1067 | + // flush if buffer exceed max size. | ||
| 1068 | + if (ab->size > SRS_HLS_AUDIO_CACHE_SIZE) { | ||
| 1069 | + if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) { | ||
| 1070 | + return ret; | ||
| 1071 | + } | ||
| 1072 | + } | ||
| 1073 | + // TODO: config it. | ||
| 1074 | + // in ms, audio delay to flush the audios. | ||
| 1075 | + int64_t audio_delay = SRS_CONF_DEFAULT_AAC_DELAY; | ||
| 1076 | + // flush if audio delay exceed | ||
| 1077 | + if (pts - audio_buffer_start_pts > audio_delay * 90) { | ||
| 1078 | + if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) { | ||
| 1079 | + return ret; | ||
| 1080 | + } | ||
| 1081 | + } | ||
| 1082 | + | ||
| 1083 | + return ret; | ||
| 1084 | +} | ||
| 1085 | + | ||
| 1086 | +int SrsHls::on_video(SrsSharedPtrMessage* video) | ||
| 1087 | +{ | ||
| 1088 | + int ret = ERROR_SUCCESS; | ||
| 1089 | + | ||
| 1090 | + SrsAutoFree(SrsSharedPtrMessage, video, false); | ||
| 1091 | + | ||
| 1092 | + // TODO: maybe donot need to demux the avc? | ||
| 1093 | + if (!hls_enabled) { | ||
| 1094 | + return ret; | ||
| 1095 | + } | ||
| 1096 | + | ||
| 1097 | + sample->clear(); | ||
| 1098 | + if ((ret = codec->video_avc_demux(video->payload, video->size, sample)) != ERROR_SUCCESS) { | ||
| 1099 | + return ret; | ||
| 1100 | + } | ||
| 1101 | + | ||
| 1102 | + if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
| 1103 | + return ret; | ||
| 1104 | + } | ||
| 1105 | + | ||
| 1106 | + // ignore sequence header | ||
| 1107 | + if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | ||
| 1108 | + return ret; | ||
| 1109 | + } | ||
| 1110 | + | ||
| 1111 | + int64_t corrected_time = 0; | ||
| 1112 | + if ((ret = jitter->correct(video, 0, 0, &corrected_time)) != ERROR_SUCCESS) { | ||
| 1113 | + return ret; | ||
| 1114 | + } | ||
| 1115 | + | ||
| 1116 | + // write video to cache. | ||
| 1117 | + if ((ret = cache_video()) != ERROR_SUCCESS) { | ||
| 1118 | + return ret; | ||
| 1119 | + } | ||
| 1120 | + | ||
| 1121 | + vf->dts = corrected_time * 90; | ||
| 1122 | + vf->pts = vf->dts + sample->cts * 90; | ||
| 1123 | + vf->pid = TS_VIDEO_PID; | ||
| 1124 | + vf->sid = TS_VIDEO_AVC; | ||
| 1125 | + vf->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; | ||
| 1126 | + | ||
| 1127 | + // flush video when got one | ||
| 1128 | + if ((ret = muxer->flush_video(af, ab, vf, vb)) != ERROR_SUCCESS) { | ||
| 1129 | + srs_error("m3u8 muxer flush video failed. ret=%d", ret); | ||
| 1130 | + return ret; | ||
| 1131 | + } | ||
| 1132 | + | ||
| 1133 | + return ret; | ||
| 1134 | +} | ||
| 1135 | + | ||
| 1136 | +int SrsHls::cache_audio() | ||
| 1043 | { | 1137 | { |
| 1044 | int ret = ERROR_SUCCESS; | 1138 | int ret = ERROR_SUCCESS; |
| 1045 | 1139 | ||
| @@ -1099,19 +1193,19 @@ int SrsHls::write_audio() | @@ -1099,19 +1193,19 @@ int SrsHls::write_audio() | ||
| 1099 | adts_header[5] |= 0x1f; | 1193 | adts_header[5] |= 0x1f; |
| 1100 | 1194 | ||
| 1101 | // copy to audio buffer | 1195 | // copy to audio buffer |
| 1102 | - audio_buffer->append(adts_header, sizeof(adts_header)); | ||
| 1103 | - audio_buffer->append(buf->bytes, buf->size); | 1196 | + ab->append(adts_header, sizeof(adts_header)); |
| 1197 | + ab->append(buf->bytes, buf->size); | ||
| 1104 | } | 1198 | } |
| 1105 | 1199 | ||
| 1106 | return ret; | 1200 | return ret; |
| 1107 | } | 1201 | } |
| 1108 | 1202 | ||
| 1109 | -int SrsHls::write_video() | 1203 | +int SrsHls::cache_video() |
| 1110 | { | 1204 | { |
| 1111 | int ret = ERROR_SUCCESS; | 1205 | int ret = ERROR_SUCCESS; |
| 1112 | 1206 | ||
| 1113 | static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; | 1207 | static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; |
| 1114 | - video_buffer->append(aud_nal, sizeof(aud_nal)); | 1208 | + vb->append(aud_nal, sizeof(aud_nal)); |
| 1115 | 1209 | ||
| 1116 | bool sps_pps_sent = false; | 1210 | bool sps_pps_sent = false; |
| 1117 | for (int i = 0; i < sample->nb_buffers; i++) { | 1211 | for (int i = 0; i < sample->nb_buffers; i++) { |
| @@ -1138,21 +1232,21 @@ int SrsHls::write_video() | @@ -1138,21 +1232,21 @@ int SrsHls::write_video() | ||
| 1138 | // 5: Coded slice of an IDR picture. | 1232 | // 5: Coded slice of an IDR picture. |
| 1139 | // insert sps/pps before IDR or key frame is ok. | 1233 | // insert sps/pps before IDR or key frame is ok. |
| 1140 | if (nal_unit_type == 5 && !sps_pps_sent) { | 1234 | if (nal_unit_type == 5 && !sps_pps_sent) { |
| 1141 | - //if (video_frame->key && !sps_pps_sent) { | 1235 | + //if (vf->key && !sps_pps_sent) { |
| 1142 | sps_pps_sent = true; | 1236 | sps_pps_sent = true; |
| 1143 | 1237 | ||
| 1144 | // ngx_rtmp_hls_append_sps_pps | 1238 | // ngx_rtmp_hls_append_sps_pps |
| 1145 | if (codec->sequenceParameterSetLength > 0) { | 1239 | if (codec->sequenceParameterSetLength > 0) { |
| 1146 | // AnnexB prefix | 1240 | // AnnexB prefix |
| 1147 | - video_buffer->append(aud_nal, 4); | 1241 | + vb->append(aud_nal, 4); |
| 1148 | // sps | 1242 | // sps |
| 1149 | - video_buffer->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); | 1243 | + vb->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); |
| 1150 | } | 1244 | } |
| 1151 | if (codec->pictureParameterSetLength > 0) { | 1245 | if (codec->pictureParameterSetLength > 0) { |
| 1152 | // AnnexB prefix | 1246 | // AnnexB prefix |
| 1153 | - video_buffer->append(aud_nal, 4); | 1247 | + vb->append(aud_nal, 4); |
| 1154 | // pps | 1248 | // pps |
| 1155 | - video_buffer->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); | 1249 | + vb->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); |
| 1156 | } | 1250 | } |
| 1157 | } | 1251 | } |
| 1158 | 1252 | ||
| @@ -1164,30 +1258,12 @@ int SrsHls::write_video() | @@ -1164,30 +1258,12 @@ int SrsHls::write_video() | ||
| 1164 | if (i == 0) { | 1258 | if (i == 0) { |
| 1165 | p = aud_nal; | 1259 | p = aud_nal; |
| 1166 | } | 1260 | } |
| 1167 | - video_buffer->append(p, end - p); | 1261 | + vb->append(p, end - p); |
| 1168 | 1262 | ||
| 1169 | // sample data | 1263 | // sample data |
| 1170 | - video_buffer->append(buf->bytes, buf->size); | ||
| 1171 | - } | ||
| 1172 | - | ||
| 1173 | - return ret; | ||
| 1174 | -} | ||
| 1175 | - | ||
| 1176 | -int SrsHls::flush_audio() | ||
| 1177 | -{ | ||
| 1178 | - int ret = ERROR_SUCCESS; | ||
| 1179 | - | ||
| 1180 | - if (audio_buffer->size <= 0) { | ||
| 1181 | - return ret; | 1264 | + vb->append(buf->bytes, buf->size); |
| 1182 | } | 1265 | } |
| 1183 | 1266 | ||
| 1184 | - if ((ret = current->muxer->write_audio(audio_frame, audio_buffer)) != ERROR_SUCCESS) { | ||
| 1185 | - return ret; | ||
| 1186 | - } | ||
| 1187 | - | ||
| 1188 | - // write success, clear and free the buffer | ||
| 1189 | - audio_buffer->free(); | ||
| 1190 | - | ||
| 1191 | return ret; | 1267 | return ret; |
| 1192 | } | 1268 | } |
| 1193 | 1269 |
| @@ -74,6 +74,22 @@ public: | @@ -74,6 +74,22 @@ public: | ||
| 74 | virtual void on_buffer_continue(); | 74 | virtual void on_buffer_continue(); |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | +//TODO: refine the ts muxer, do more jobs. | ||
| 78 | +class SrsTSMuxer | ||
| 79 | +{ | ||
| 80 | +private: | ||
| 81 | + int fd; | ||
| 82 | + std::string path; | ||
| 83 | +public: | ||
| 84 | + SrsTSMuxer(); | ||
| 85 | + virtual ~SrsTSMuxer(); | ||
| 86 | +public: | ||
| 87 | + virtual int open(std::string _path); | ||
| 88 | + virtual int write_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab); | ||
| 89 | + virtual int write_video(SrsMpegtsFrame* vf, SrsCodecBuffer* vb); | ||
| 90 | + virtual void close(); | ||
| 91 | +}; | ||
| 92 | + | ||
| 77 | /** | 93 | /** |
| 78 | * 3.3.2. EXTINF | 94 | * 3.3.2. EXTINF |
| 79 | * The EXTINF tag specifies the duration of a media segment. | 95 | * The EXTINF tag specifies the duration of a media segment. |
| @@ -102,33 +118,15 @@ struct SrsM3u8Segment | @@ -102,33 +118,15 @@ struct SrsM3u8Segment | ||
| 102 | virtual double update_duration(int64_t video_stream_dts); | 118 | virtual double update_duration(int64_t video_stream_dts); |
| 103 | }; | 119 | }; |
| 104 | 120 | ||
| 105 | -//TODO: refine the ts muxer, do more jobs. | ||
| 106 | -class SrsTSMuxer | ||
| 107 | -{ | ||
| 108 | -private: | ||
| 109 | - int fd; | ||
| 110 | - std::string path; | ||
| 111 | - bool _fresh; | ||
| 112 | -public: | ||
| 113 | - SrsTSMuxer(); | ||
| 114 | - virtual ~SrsTSMuxer(); | ||
| 115 | -public: | ||
| 116 | - virtual int open(std::string _path); | ||
| 117 | - virtual int write_audio(SrsMpegtsFrame* audio_frame, SrsCodecBuffer* audio_buffer); | ||
| 118 | - virtual int write_video(SrsMpegtsFrame* video_frame, SrsCodecBuffer* video_buffer); | ||
| 119 | - virtual void close(); | ||
| 120 | - virtual bool fresh(); | ||
| 121 | -}; | ||
| 122 | - | ||
| 123 | /** | 121 | /** |
| 124 | -* write m3u8 hls. | 122 | +* muxer the m3u8 and ts files. |
| 125 | */ | 123 | */ |
| 126 | -class SrsHls | 124 | +class SrsM3u8Muxer |
| 127 | { | 125 | { |
| 128 | private: | 126 | private: |
| 129 | - std::string vhost; | ||
| 130 | - std::string stream; | ||
| 131 | std::string app; | 127 | std::string app; |
| 128 | + std::string stream; | ||
| 129 | +private: | ||
| 132 | std::string hls_path; | 130 | std::string hls_path; |
| 133 | int hls_fragment; | 131 | int hls_fragment; |
| 134 | int hls_window; | 132 | int hls_window; |
| @@ -144,16 +142,37 @@ private: | @@ -144,16 +142,37 @@ private: | ||
| 144 | * current writing segment. | 142 | * current writing segment. |
| 145 | */ | 143 | */ |
| 146 | SrsM3u8Segment* current; | 144 | SrsM3u8Segment* current; |
| 147 | - // current frame and buffer | ||
| 148 | - SrsMpegtsFrame* audio_frame; | ||
| 149 | - SrsCodecBuffer* audio_buffer; | ||
| 150 | - SrsMpegtsFrame* video_frame; | ||
| 151 | - SrsCodecBuffer* video_buffer; | ||
| 152 | // last known dts | 145 | // last known dts |
| 153 | int64_t video_stream_dts; | 146 | int64_t video_stream_dts; |
| 147 | +public: | ||
| 148 | + SrsM3u8Muxer(); | ||
| 149 | + virtual ~SrsM3u8Muxer(); | ||
| 150 | +public: | ||
| 151 | + virtual int update_config(std::string _app, std::string _stream, std::string path, int fragment, int window); | ||
| 152 | + virtual int segment_open(); | ||
| 153 | + virtual int flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab); | ||
| 154 | + virtual int flush_video(SrsMpegtsFrame* af, SrsCodecBuffer* ab, SrsMpegtsFrame* vf, SrsCodecBuffer* vb); | ||
| 155 | + virtual int segment_close(); | ||
| 156 | +private: | ||
| 157 | + virtual int refresh_m3u8(); | ||
| 158 | + virtual int _refresh_m3u8(int& fd, std::string m3u8_file); | ||
| 159 | + virtual int create_dir(); | ||
| 160 | +}; | ||
| 161 | + | ||
| 162 | +/** | ||
| 163 | +* write m3u8 hls. | ||
| 164 | +*/ | ||
| 165 | +class SrsHls | ||
| 166 | +{ | ||
| 167 | +private: | ||
| 168 | + SrsM3u8Muxer* muxer; | ||
| 169 | + // current frame and buffer | ||
| 170 | + SrsMpegtsFrame* af; | ||
| 171 | + SrsCodecBuffer* ab; | ||
| 172 | + SrsMpegtsFrame* vf; | ||
| 173 | + SrsCodecBuffer* vb; | ||
| 174 | + // the audio cache buffer start pts, to flush audio if full. | ||
| 154 | int64_t audio_buffer_start_pts; | 175 | int64_t audio_buffer_start_pts; |
| 155 | - // in ms, audio delay to flush the audios. | ||
| 156 | - int64_t audio_delay; | ||
| 157 | private: | 176 | private: |
| 158 | bool hls_enabled; | 177 | bool hls_enabled; |
| 159 | SrsCodec* codec; | 178 | SrsCodec* codec; |
| @@ -170,14 +189,8 @@ public: | @@ -170,14 +189,8 @@ public: | ||
| 170 | virtual int on_audio(SrsSharedPtrMessage* audio); | 189 | virtual int on_audio(SrsSharedPtrMessage* audio); |
| 171 | virtual int on_video(SrsSharedPtrMessage* video); | 190 | virtual int on_video(SrsSharedPtrMessage* video); |
| 172 | private: | 191 | private: |
| 173 | - virtual int reopen(); | ||
| 174 | - virtual int refresh_m3u8(); | ||
| 175 | - virtual int _refresh_m3u8(int& fd, std::string m3u8_file); | ||
| 176 | - virtual int create_dir(); | ||
| 177 | -private: | ||
| 178 | - virtual int write_audio(); | ||
| 179 | - virtual int write_video(); | ||
| 180 | - virtual int flush_audio(); | 192 | + virtual int cache_audio(); |
| 193 | + virtual int cache_video(); | ||
| 181 | }; | 194 | }; |
| 182 | 195 | ||
| 183 | #endif | 196 | #endif |
-
请 注册 或 登录 后发表评论