正在显示
2 个修改的文件
包含
454 行增加
和
365 行删除
@@ -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; | 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); | ||
486 | + | ||
487 | + return duration; | ||
488 | +} | ||
521 | 489 | ||
522 | - audio_buffer = new SrsCodecBuffer(); | ||
523 | - video_buffer = new SrsCodecBuffer(); | 490 | +SrsHlsAacJitter::SrsHlsAacJitter() |
491 | +{ | ||
492 | + base_pts = 0; | ||
493 | + nb_samples = 0; | ||
524 | 494 | ||
525 | - audio_frame = new SrsMpegtsFrame(); | ||
526 | - video_frame = new SrsMpegtsFrame(); | 495 | + // TODO: config it, 0 means no adjust |
496 | + sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; | ||
527 | } | 497 | } |
528 | 498 | ||
529 | -SrsHls::~SrsHls() | 499 | +SrsM3u8Muxer::SrsM3u8Muxer() |
530 | { | 500 | { |
531 | - srs_freep(codec); | ||
532 | - srs_freep(sample); | ||
533 | - srs_freep(jitter); | ||
534 | - srs_freep(aac_jitter); | 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,274 +514,154 @@ SrsHls::~SrsHls() | @@ -541,274 +514,154 @@ SrsHls::~SrsHls() | ||
541 | segments.clear(); | 514 | segments.clear(); |
542 | 515 | ||
543 | srs_freep(current); | 516 | srs_freep(current); |
544 | - | ||
545 | - audio_buffer->free(); | ||
546 | - video_buffer->free(); | ||
547 | - | ||
548 | - srs_freep(audio_buffer); | ||
549 | - srs_freep(video_buffer); | ||
550 | - | ||
551 | - srs_freep(audio_frame); | ||
552 | - srs_freep(video_frame); | ||
553 | } | 517 | } |
554 | 518 | ||
555 | -int SrsHls::on_publish(SrsRequest* req) | ||
556 | -{ | 519 | +int SrsM3u8Muxer::update_config( |
520 | + std::string _app, std::string _stream, | ||
521 | + std::string path, int fragment, int window | ||
522 | +) { | ||
557 | int ret = ERROR_SUCCESS; | 523 | int ret = ERROR_SUCCESS; |
558 | 524 | ||
559 | - vhost = req->vhost; | ||
560 | - stream = req->stream; | ||
561 | - app = req->app; | ||
562 | - | ||
563 | - // TODO: subscribe the reload event. | ||
564 | - | ||
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; | ||
571 | - } | ||
572 | - | ||
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 | - } | ||
579 | - | ||
580 | - if ((ret = reopen()) != ERROR_SUCCESS) { | ||
581 | - return ret; | ||
582 | - } | 525 | + app = _app; |
526 | + stream = _stream; | ||
527 | + hls_path = path; | ||
528 | + hls_fragment = fragment; | ||
529 | + hls_window = window; | ||
583 | 530 | ||
584 | return ret; | 531 | return ret; |
585 | } | 532 | } |
586 | 533 | ||
587 | -void SrsHls::on_unpublish() | ||
588 | -{ | ||
589 | - hls_enabled = false; | ||
590 | -} | ||
591 | - | ||
592 | -int SrsHls::on_meta_data(SrsOnMetaDataPacket* metadata) | 534 | +int SrsM3u8Muxer::segment_open() |
593 | { | 535 | { |
594 | int ret = ERROR_SUCCESS; | 536 | int ret = ERROR_SUCCESS; |
595 | 537 | ||
596 | - if (!metadata || !metadata->metadata) { | ||
597 | - srs_trace("no metadata persent, hls ignored it."); | ||
598 | - return ret; | ||
599 | - } | ||
600 | - | ||
601 | - SrsAmf0Object* obj = metadata->metadata; | ||
602 | - if (obj->size() <= 0) { | ||
603 | - srs_trace("no metadata persent, hls ignored it."); | 538 | + // TODO: create all parents dirs. |
539 | + // create dir for app. | ||
540 | + if ((ret = create_dir()) != ERROR_SUCCESS) { | ||
604 | return ret; | 541 | return ret; |
605 | } | 542 | } |
606 | 543 | ||
607 | - // finger out the codec info from metadata if possible. | ||
608 | - SrsAmf0Any* prop = NULL; | ||
609 | - | ||
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; | ||
642 | -} | 544 | + // when segment open, the current segment must be NULL. |
545 | + srs_assert(!current); | ||
643 | 546 | ||
644 | -int SrsHls::on_audio(SrsSharedPtrMessage* audio) | ||
645 | -{ | ||
646 | - int ret = ERROR_SUCCESS; | 547 | + // new segment. |
548 | + current = new SrsM3u8Segment(); | ||
549 | + current->sequence_no = file_index++; | ||
550 | + current->segment_start_dts = video_stream_dts; | ||
647 | 551 | ||
648 | - SrsAutoFree(SrsSharedPtrMessage, audio, false); | 552 | + // generate filename. |
553 | + char filename[128]; | ||
554 | + snprintf(filename, sizeof(filename), | ||
555 | + "%s-%d.ts", stream.c_str(), current->sequence_no); | ||
649 | 556 | ||
650 | - // TODO: maybe donot need to demux the aac? | ||
651 | - if (!hls_enabled) { | ||
652 | - return ret; | ||
653 | - } | 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; | ||
654 | 563 | ||
655 | - sample->clear(); | ||
656 | - if ((ret = codec->audio_aac_demux(audio->payload, audio->size, sample)) != ERROR_SUCCESS) { | ||
657 | - return ret; | ||
658 | - } | 564 | + // TODO: support base url, and so on. |
565 | + current->uri = filename; | ||
659 | 566 | ||
660 | - if (codec->audio_codec_id != SrsCodecAudioAAC) { | 567 | + if ((ret = current->muxer->open(current->full_path)) != ERROR_SUCCESS) { |
568 | + srs_error("open hls muxer failed. ret=%d", ret); | ||
661 | return ret; | 569 | return ret; |
662 | } | 570 | } |
571 | + srs_info("open HLS muxer success. vhost=%s, path=%s", | ||
572 | + vhost.c_str(), current->full_path.c_str()); | ||
663 | 573 | ||
664 | - // ignore sequence header | ||
665 | - if (sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) { | ||
666 | return ret; | 574 | return ret; |
667 | - } | 575 | +} |
668 | 576 | ||
669 | - int64_t corrected_time = 0; | ||
670 | - if ((ret = jitter->correct(audio, 0, 0, &corrected_time)) != ERROR_SUCCESS) { | ||
671 | - return ret; | ||
672 | - } | 577 | +int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab) |
578 | +{ | ||
579 | + int ret = ERROR_SUCCESS; | ||
673 | 580 | ||
674 | srs_assert(current); | 581 | srs_assert(current); |
675 | 582 | ||
676 | - // the pts calc from rtmp/flv header. | ||
677 | - int64_t pts = corrected_time * 90; | ||
678 | - | ||
679 | - // flush if audio delay exceed | ||
680 | - if (pts - audio_buffer_start_pts > audio_delay * 90) { | ||
681 | - if ((ret = flush_audio()) != ERROR_SUCCESS) { | 583 | + if (ab->size <= 0) { |
682 | return ret; | 584 | return ret; |
683 | } | 585 | } |
684 | - } | ||
685 | - | ||
686 | - // start buffer, set the audio_frame | ||
687 | - if (audio_buffer->size == 0) { | ||
688 | - pts = aac_jitter->on_buffer_start(pts, sample->sound_rate); | ||
689 | - | ||
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(); | ||
695 | - } | ||
696 | 586 | ||
697 | - // write audio to cache. | ||
698 | - if ((ret = write_audio()) != ERROR_SUCCESS) { | 587 | + if ((ret = current->muxer->write_audio(af, ab)) != ERROR_SUCCESS) { |
699 | return ret; | 588 | return ret; |
700 | } | 589 | } |
701 | 590 | ||
702 | - // write cache to file. | ||
703 | - if (audio_buffer->size > 1024 * 1024) { | ||
704 | - if ((ret = flush_audio()) != ERROR_SUCCESS) { | ||
705 | - return ret; | ||
706 | - } | ||
707 | - } | 591 | + // write success, clear and free the buffer |
592 | + ab->free(); | ||
708 | 593 | ||
709 | return ret; | 594 | return ret; |
710 | } | 595 | } |
711 | 596 | ||
712 | -int SrsHls::on_video(SrsSharedPtrMessage* video) | 597 | +int SrsM3u8Muxer::flush_video( |
598 | + SrsMpegtsFrame* af, SrsCodecBuffer* ab, | ||
599 | + SrsMpegtsFrame* vf, SrsCodecBuffer* vb) | ||
713 | { | 600 | { |
714 | int ret = ERROR_SUCCESS; | 601 | int ret = ERROR_SUCCESS; |
715 | 602 | ||
716 | - SrsAutoFree(SrsSharedPtrMessage, video, false); | ||
717 | - | ||
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 | - } | 603 | + srs_assert(current); |
727 | 604 | ||
728 | - if (codec->video_codec_id != SrsCodecVideoAVC) { | ||
729 | - return ret; | ||
730 | - } | 605 | + video_stream_dts = vf->dts; |
731 | 606 | ||
732 | - // ignore sequence header | ||
733 | - if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { | 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); | ||
734 | return ret; | 613 | return ret; |
735 | } | 614 | } |
615 | + */ | ||
736 | 616 | ||
737 | - int64_t corrected_time = 0; | ||
738 | - if ((ret = jitter->correct(video, 0, 0, &corrected_time)) != ERROR_SUCCESS) { | 617 | + if ((ret = segment_close()) != ERROR_SUCCESS) { |
618 | + srs_error("m3u8 muxer close segment failed. ret=%d", ret); | ||
739 | return ret; | 619 | return ret; |
740 | } | 620 | } |
741 | 621 | ||
742 | - // write video to cache. | ||
743 | - if ((ret = write_video()) != ERROR_SUCCESS) { | 622 | + if ((ret = segment_open()) != ERROR_SUCCESS) { |
623 | + srs_error("m3u8 muxer open segment failed. ret=%d", ret); | ||
744 | return ret; | 624 | return ret; |
745 | } | 625 | } |
746 | 626 | ||
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) { | 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); | ||
758 | return ret; | 633 | return ret; |
759 | } | 634 | } |
760 | } | 635 | } |
761 | - } | ||
762 | 636 | ||
763 | srs_assert(current); | 637 | srs_assert(current); |
764 | // update the duration of segment. | 638 | // update the duration of segment. |
765 | current->update_duration(video_stream_dts); | 639 | current->update_duration(video_stream_dts); |
766 | 640 | ||
767 | - if ((ret = current->muxer->write_video(video_frame, video_buffer)) != ERROR_SUCCESS) { | 641 | + if ((ret = current->muxer->write_video(vf, vb)) != ERROR_SUCCESS) { |
768 | return ret; | 642 | return ret; |
769 | } | 643 | } |
770 | 644 | ||
771 | // write success, clear and free the buffer | 645 | // write success, clear and free the buffer |
772 | - video_buffer->free(); | 646 | + vb->free(); |
773 | 647 | ||
774 | return ret; | 648 | return ret; |
775 | } | 649 | } |
776 | 650 | ||
777 | -int SrsHls::reopen() | 651 | +int SrsM3u8Muxer::segment_close() |
778 | { | 652 | { |
779 | int ret = ERROR_SUCCESS; | 653 | int ret = ERROR_SUCCESS; |
780 | 654 | ||
781 | - // try to open the HLS muxer | ||
782 | - if (!config->get_hls_enabled(vhost)) { | ||
783 | - return ret; | ||
784 | - } | 655 | + // when close current segment, the current segment must not be NULL. |
656 | + srs_assert(current); | ||
785 | 657 | ||
786 | - // TODO: check the audio and video, ensure both exsists. | ||
787 | - // for use fixed mpegts header specifeid the audio and video pid. | 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()); | ||
788 | 662 | ||
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); | 663 | + // valid, add to segments. |
664 | + segments.push_back(current); | ||
812 | 665 | ||
813 | srs_trace("reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", | 666 | srs_trace("reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", |
814 | current->sequence_no, current->uri.c_str(), current->duration, | 667 | current->sequence_no, current->uri.c_str(), current->duration, |
@@ -855,47 +708,11 @@ int SrsHls::reopen() | @@ -855,47 +708,11 @@ int SrsHls::reopen() | ||
855 | srs_error("refresh m3u8 failed. ret=%d", ret); | 708 | srs_error("refresh m3u8 failed. ret=%d", ret); |
856 | return ret; | 709 | return ret; |
857 | } | 710 | } |
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 | 711 | ||
895 | return ret; | 712 | return ret; |
896 | } | 713 | } |
897 | 714 | ||
898 | -int SrsHls::refresh_m3u8() | 715 | +int SrsM3u8Muxer::refresh_m3u8() |
899 | { | 716 | { |
900 | int ret = ERROR_SUCCESS; | 717 | int ret = ERROR_SUCCESS; |
901 | 718 | ||
@@ -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 |
-
请 注册 或 登录 后发表评论