winlin

refine the hls module, clear logic dead code, rename TSCache to HlsCache, M3u8Mu…

…xer to HlsMuxer. that is, make it to more readable.
@@ -212,7 +212,7 @@ if [ $SRS_ARM_UBUNTU12 = YES ]; then @@ -212,7 +212,7 @@ if [ $SRS_ARM_UBUNTU12 = YES ]; then
212 unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 && 212 unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 &&
213 patch -p0 < ../../3rdparty/patches/1.st.arm.patch && 213 patch -p0 < ../../3rdparty/patches/1.st.arm.patch &&
214 make CC=${SrsArmCC} AR=${SrsArmAR} LD=${SrsArmLD} RANDLIB=${SrsArmRANDLIB} linux-debug && 214 make CC=${SrsArmCC} AR=${SrsArmAR} LD=${SrsArmLD} RANDLIB=${SrsArmRANDLIB} linux-debug &&
215 - cd .. && rm -f st && ln -sf st-1.9/obj st && 215 + cd .. && rm -rf st && ln -sf st-1.9/obj st &&
216 cd .. && touch ${SRS_OBJS}/_flag.st.arm.tmp 216 cd .. && touch ${SRS_OBJS}/_flag.st.arm.tmp
217 ) 217 )
218 fi 218 fi
@@ -226,7 +226,7 @@ else @@ -226,7 +226,7 @@ else
226 rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} && 226 rm -rf ${SRS_OBJS}/st-1.9 && cd ${SRS_OBJS} &&
227 unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 && 227 unzip -q ../3rdparty/st-1.9.zip && cd st-1.9 &&
228 make linux-debug && 228 make linux-debug &&
229 - cd .. && rm -f st && ln -sf st-1.9/obj st && 229 + cd .. && rm -rf st && ln -sf st-1.9/obj st &&
230 cd .. && rm -f ${SRS_OBJS}/_flag.st.arm.tmp 230 cd .. && rm -f ${SRS_OBJS}/_flag.st.arm.tmp
231 ) 231 )
232 fi 232 fi
@@ -250,7 +250,7 @@ if [ $SRS_HTTP_CALLBACK = YES ]; then @@ -250,7 +250,7 @@ if [ $SRS_HTTP_CALLBACK = YES ]; then
250 sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile && 250 sed -i "s/CPPFLAGS_FAST +=.*$/CPPFLAGS_FAST = \$\(CPPFLAGS_DEBUG\)/g" Makefile &&
251 sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile && 251 sed -i "s/CFLAGS_FAST =.*$/CFLAGS_FAST = \$\(CFLAGS_DEBUG\)/g" Makefile &&
252 make package && 252 make package &&
253 - cd .. && rm -f hp && ln -sf http-parser-2.1 hp 253 + cd .. && rm -rf hp && ln -sf http-parser-2.1 hp
254 ) 254 )
255 fi 255 fi
256 # check status 256 # check status
@@ -282,7 +282,7 @@ if [ $__SRS_BUILD_NGINX = YES ]; then @@ -282,7 +282,7 @@ if [ $__SRS_BUILD_NGINX = YES ]; then
282 rm -rf ${SRS_OBJS}/nginx-1.5.7 && cd ${SRS_OBJS} && 282 rm -rf ${SRS_OBJS}/nginx-1.5.7 && cd ${SRS_OBJS} &&
283 unzip -q ../3rdparty/nginx-1.5.7.zip && cd nginx-1.5.7 && 283 unzip -q ../3rdparty/nginx-1.5.7.zip && cd nginx-1.5.7 &&
284 ./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install && 284 ./configure --prefix=`pwd`/_release && make ${SRS_JOBS} && make install &&
285 - cd .. && ln -sf nginx-1.5.7/_release nginx 285 + cd .. && rm -rf nginx && ln -sf nginx-1.5.7/_release nginx
286 ) 286 )
287 fi 287 fi
288 # check status 288 # check status
@@ -356,14 +356,14 @@ else @@ -356,14 +356,14 @@ else
356 fi 356 fi
357 357
358 echo "link players to cherrypy static-dir" 358 echo "link players to cherrypy static-dir"
359 -rm -f research/api-server/static-dir/players && 359 +rm -rf research/api-server/static-dir/players &&
360 ln -sf `pwd`/research/players research/api-server/static-dir/players && 360 ln -sf `pwd`/research/players research/api-server/static-dir/players &&
361 rm -f research/api-server/static-dir/crossdomain.xml && 361 rm -f research/api-server/static-dir/crossdomain.xml &&
362 ln -sf `pwd`/research/players/crossdomain.xml research/api-server/static-dir/crossdomain.xml && 362 ln -sf `pwd`/research/players/crossdomain.xml research/api-server/static-dir/crossdomain.xml &&
363 -rm -f research/api-server/static-dir/live && 363 +rm -rf research/api-server/static-dir/live &&
364 mkdir -p `pwd`/${SRS_OBJS}/nginx/html/live && 364 mkdir -p `pwd`/${SRS_OBJS}/nginx/html/live &&
365 ln -sf `pwd`/${SRS_OBJS}/nginx/html/live research/api-server/static-dir/live && 365 ln -sf `pwd`/${SRS_OBJS}/nginx/html/live research/api-server/static-dir/live &&
366 -rm -f research/api-server/static-dir/forward && 366 +rm -rf research/api-server/static-dir/forward &&
367 mkdir -p `pwd`/${SRS_OBJS}/nginx/html/forward && 367 mkdir -p `pwd`/${SRS_OBJS}/nginx/html/forward &&
368 ln -sf `pwd`/${SRS_OBJS}/nginx/html/forward research/api-server/static-dir/forward 368 ln -sf `pwd`/${SRS_OBJS}/nginx/html/forward research/api-server/static-dir/forward
369 369
@@ -410,7 +410,7 @@ if [ $SRS_SSL = YES ]; then @@ -410,7 +410,7 @@ if [ $SRS_SSL = YES ]; then
410 unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f && 410 unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f &&
411 ./Configure --prefix=`pwd`/_release -no-shared no-asm linux-armv4 && 411 ./Configure --prefix=`pwd`/_release -no-shared no-asm linux-armv4 &&
412 make CC=${SrsArmCC} GCC=${SrsArmGCC} AR="${SrsArmAR} r" LD=${SrsArmLD} LINK=${SrsArmGCC} RANDLIB=${SrsArmRANDLIB} && make install && 412 make CC=${SrsArmCC} GCC=${SrsArmGCC} AR="${SrsArmAR} r" LD=${SrsArmLD} LINK=${SrsArmGCC} RANDLIB=${SrsArmRANDLIB} && make install &&
413 - cd .. && ln -sf openssl-1.0.1f/_release openssl && 413 + cd .. && rm -rf openssl && ln -sf openssl-1.0.1f/_release openssl &&
414 cd .. && touch ${SRS_OBJS}/_flag.ssl.arm.tmp 414 cd .. && touch ${SRS_OBJS}/_flag.ssl.arm.tmp
415 ) 415 )
416 fi 416 fi
@@ -425,7 +425,7 @@ if [ $SRS_SSL = YES ]; then @@ -425,7 +425,7 @@ if [ $SRS_SSL = YES ]; then
425 unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f && 425 unzip -q ../3rdparty/openssl-1.0.1f.zip && cd openssl-1.0.1f &&
426 ./config --prefix=`pwd`/_release -no-shared && 426 ./config --prefix=`pwd`/_release -no-shared &&
427 make && make install && 427 make && make install &&
428 - cd .. && ln -sf openssl-1.0.1f/_release openssl && 428 + cd .. && rm -rf openssl && ln -sf openssl-1.0.1f/_release openssl &&
429 cd .. && rm -f ${SRS_OBJS}/_flag.ssl.arm.tmp 429 cd .. && rm -f ${SRS_OBJS}/_flag.ssl.arm.tmp
430 ) 430 )
431 fi 431 fi
@@ -453,7 +453,7 @@ if [ $SRS_FFMPEG = YES ]; then @@ -453,7 +453,7 @@ if [ $SRS_FFMPEG = YES ]; then
453 cd ${SRS_OBJS} && pwd_dir=`pwd` && 453 cd ${SRS_OBJS} && pwd_dir=`pwd` &&
454 rm -rf ffmepg.src && mkdir -p ffmpeg.src && cd ffmpeg.src && 454 rm -rf ffmepg.src && mkdir -p ffmpeg.src && cd ffmpeg.src &&
455 rm -f build_ffmpeg.sh && ln -sf ../../auto/build_ffmpeg.sh && . build_ffmpeg.sh && 455 rm -f build_ffmpeg.sh && ln -sf ../../auto/build_ffmpeg.sh && . build_ffmpeg.sh &&
456 - cd ${pwd_dir} && ln -sf ffmpeg.src/_release ffmpeg 456 + cd ${pwd_dir} && rm -rf ffmpeg && ln -sf ffmpeg.src/_release ffmpeg
457 ) 457 )
458 fi 458 fi
459 # check status 459 # check status
@@ -491,7 +491,7 @@ if [ $SRS_UTEST = YES ]; then @@ -491,7 +491,7 @@ if [ $SRS_UTEST = YES ]; then
491 ( 491 (
492 rm -rf ${SRS_OBJS}/gtest-1.6.0 && cd ${SRS_OBJS} && 492 rm -rf ${SRS_OBJS}/gtest-1.6.0 && cd ${SRS_OBJS} &&
493 unzip -q ../3rdparty/gtest-1.6.0.zip && 493 unzip -q ../3rdparty/gtest-1.6.0.zip &&
494 - rm -f gtest && ln -sf gtest-1.6.0 gtest 494 + rm -rf gtest && ln -sf gtest-1.6.0 gtest
495 ) 495 )
496 fi 496 fi
497 # check status 497 # check status
@@ -511,8 +511,8 @@ if [ $SRS_GPERF = YES ]; then @@ -511,8 +511,8 @@ if [ $SRS_GPERF = YES ]; then
511 rm -rf ${SRS_OBJS}/gperftools-2.1 && cd ${SRS_OBJS} && 511 rm -rf ${SRS_OBJS}/gperftools-2.1 && cd ${SRS_OBJS} &&
512 unzip -q ../3rdparty/gperftools-2.1.zip && cd gperftools-2.1 && 512 unzip -q ../3rdparty/gperftools-2.1.zip && cd gperftools-2.1 &&
513 ./configure --prefix=`pwd`/_release --enable-frame-pointers && make ${SRS_JOBS} && make install && 513 ./configure --prefix=`pwd`/_release --enable-frame-pointers && make ${SRS_JOBS} && make install &&
514 - cd .. && rm -f gperf && ln -sf gperftools-2.1/_release gperf &&  
515 - rm -f pprof && ln -sf gperf/bin/pprof pprof 514 + cd .. && rm -rf gperf && ln -sf gperftools-2.1/_release gperf &&
  515 + rm -rf pprof && ln -sf gperf/bin/pprof pprof
516 ) 516 )
517 fi 517 fi
518 # check status 518 # check status
@@ -121,14 +121,15 @@ u_int8_t mpegts_header[] = { @@ -121,14 +121,15 @@ u_int8_t mpegts_header[] = {
121 }; 121 };
122 122
123 // @see: ngx_rtmp_SrsMpegtsFrame_t 123 // @see: ngx_rtmp_SrsMpegtsFrame_t
124 -struct SrsMpegtsFrame 124 +class SrsMpegtsFrame
125 { 125 {
126 - int64_t pts;  
127 - int64_t dts;  
128 - int pid;  
129 - int sid;  
130 - int cc;  
131 - bool key; 126 +public:
  127 + int64_t pts;
  128 + int64_t dts;
  129 + int pid;
  130 + int sid;
  131 + int cc;
  132 + bool key;
132 133
133 SrsMpegtsFrame() 134 SrsMpegtsFrame()
134 { 135 {
@@ -467,7 +468,7 @@ void SrsTSMuxer::close() @@ -467,7 +468,7 @@ void SrsTSMuxer::close()
467 } 468 }
468 } 469 }
469 470
470 -SrsM3u8Segment::SrsM3u8Segment() 471 +SrsHlsSegment::SrsHlsSegment()
471 { 472 {
472 duration = 0; 473 duration = 0;
473 sequence_no = 0; 474 sequence_no = 0;
@@ -475,14 +476,14 @@ SrsM3u8Segment::SrsM3u8Segment() @@ -475,14 +476,14 @@ SrsM3u8Segment::SrsM3u8Segment()
475 segment_start_dts = 0; 476 segment_start_dts = 0;
476 } 477 }
477 478
478 -SrsM3u8Segment::~SrsM3u8Segment() 479 +SrsHlsSegment::~SrsHlsSegment()
479 { 480 {
480 srs_freep(muxer); 481 srs_freep(muxer);
481 } 482 }
482 483
483 -double SrsM3u8Segment::update_duration(int64_t video_stream_dts) 484 +double SrsHlsSegment::update_duration(int64_t current_frame_dts)
484 { 485 {
485 - duration = (video_stream_dts - segment_start_dts) / 90000.0; 486 + duration = (current_frame_dts - segment_start_dts) / 90000.0;
486 srs_assert(duration >= 0); 487 srs_assert(duration >= 0);
487 488
488 return duration; 489 return duration;
@@ -497,19 +498,19 @@ SrsHlsAacJitter::SrsHlsAacJitter() @@ -497,19 +498,19 @@ SrsHlsAacJitter::SrsHlsAacJitter()
497 sync_ms = SRS_CONF_DEFAULT_AAC_SYNC; 498 sync_ms = SRS_CONF_DEFAULT_AAC_SYNC;
498 } 499 }
499 500
500 -SrsM3u8Muxer::SrsM3u8Muxer() 501 +SrsHlsMuxer::SrsHlsMuxer()
501 { 502 {
502 hls_fragment = hls_window = 0; 503 hls_fragment = hls_window = 0;
503 - video_stream_dts = 0;  
504 file_index = 0; 504 file_index = 0;
505 current = NULL; 505 current = NULL;
  506 + video_count = 0;
506 } 507 }
507 508
508 -SrsM3u8Muxer::~SrsM3u8Muxer() 509 +SrsHlsMuxer::~SrsHlsMuxer()
509 { 510 {
510 - std::vector<SrsM3u8Segment*>::iterator it; 511 + std::vector<SrsHlsSegment*>::iterator it;
511 for (it = segments.begin(); it != segments.end(); ++it) { 512 for (it = segments.begin(); it != segments.end(); ++it) {
512 - SrsM3u8Segment* segment = *it; 513 + SrsHlsSegment* segment = *it;
513 srs_freep(segment); 514 srs_freep(segment);
514 } 515 }
515 segments.clear(); 516 segments.clear();
@@ -517,7 +518,7 @@ SrsM3u8Muxer::~SrsM3u8Muxer() @@ -517,7 +518,7 @@ SrsM3u8Muxer::~SrsM3u8Muxer()
517 srs_freep(current); 518 srs_freep(current);
518 } 519 }
519 520
520 -int SrsM3u8Muxer::update_config( 521 +int SrsHlsMuxer::update_config(
521 std::string _app, std::string _stream, 522 std::string _app, std::string _stream,
522 std::string path, int fragment, int window 523 std::string path, int fragment, int window
523 ) { 524 ) {
@@ -532,7 +533,7 @@ int SrsM3u8Muxer::update_config( @@ -532,7 +533,7 @@ int SrsM3u8Muxer::update_config(
532 return ret; 533 return ret;
533 } 534 }
534 535
535 -int SrsM3u8Muxer::segment_open() 536 +int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
536 { 537 {
537 int ret = ERROR_SUCCESS; 538 int ret = ERROR_SUCCESS;
538 539
@@ -541,6 +542,9 @@ int SrsM3u8Muxer::segment_open() @@ -541,6 +542,9 @@ int SrsM3u8Muxer::segment_open()
541 return ret; 542 return ret;
542 } 543 }
543 544
  545 + // reset video count for new publish session.
  546 + video_count = 0;
  547 +
544 // TODO: create all parents dirs. 548 // TODO: create all parents dirs.
545 // create dir for app. 549 // create dir for app.
546 if ((ret = create_dir()) != ERROR_SUCCESS) { 550 if ((ret = create_dir()) != ERROR_SUCCESS) {
@@ -551,9 +555,9 @@ int SrsM3u8Muxer::segment_open() @@ -551,9 +555,9 @@ int SrsM3u8Muxer::segment_open()
551 srs_assert(!current); 555 srs_assert(!current);
552 556
553 // new segment. 557 // new segment.
554 - current = new SrsM3u8Segment(); 558 + current = new SrsHlsSegment();
555 current->sequence_no = file_index++; 559 current->sequence_no = file_index++;
556 - current->segment_start_dts = video_stream_dts; 560 + current->segment_start_dts = segment_start_dts;
557 561
558 // generate filename. 562 // generate filename.
559 char filename[128]; 563 char filename[128];
@@ -580,7 +584,14 @@ int SrsM3u8Muxer::segment_open() @@ -580,7 +584,14 @@ int SrsM3u8Muxer::segment_open()
580 return ret; 584 return ret;
581 } 585 }
582 586
583 -int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab) 587 +bool SrsHlsMuxer::is_segment_overflow()
  588 +{
  589 + srs_assert(current);
  590 +
  591 + return current->duration >= hls_fragment;
  592 +}
  593 +
  594 +int SrsHlsMuxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab)
584 { 595 {
585 int ret = ERROR_SUCCESS; 596 int ret = ERROR_SUCCESS;
586 597
@@ -604,9 +615,8 @@ int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab) @@ -604,9 +615,8 @@ int SrsM3u8Muxer::flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab)
604 return ret; 615 return ret;
605 } 616 }
606 617
607 -int SrsM3u8Muxer::flush_video(  
608 - SrsMpegtsFrame* af, SrsCodecBuffer* ab,  
609 - SrsMpegtsFrame* vf, SrsCodecBuffer* vb) 618 +int SrsHlsMuxer::flush_video(
  619 + SrsMpegtsFrame* af, SrsCodecBuffer* ab, SrsMpegtsFrame* vf, SrsCodecBuffer* vb)
610 { 620 {
611 int ret = ERROR_SUCCESS; 621 int ret = ERROR_SUCCESS;
612 622
@@ -616,53 +626,19 @@ int SrsM3u8Muxer::flush_video( @@ -616,53 +626,19 @@ int SrsM3u8Muxer::flush_video(
616 return ret; 626 return ret;
617 } 627 }
618 628
619 - video_stream_dts = vf->dts;  
620 -  
621 srs_assert(current); 629 srs_assert(current);
622 - // reopen the muxer for a gop  
623 - if (vf->key && current->duration >= hls_fragment) {  
624 - // TODO: flush audio before or after segment?  
625 - /*  
626 - if ((ret = flush_audio(af, ab)) != ERROR_SUCCESS) {  
627 - srs_error("m3u8 muxer flush audio failed. ret=%d", ret);  
628 - return ret;  
629 - }  
630 - */  
631 -  
632 - if ((ret = segment_close()) != ERROR_SUCCESS) {  
633 - srs_error("m3u8 muxer close segment failed. ret=%d", ret);  
634 - return ret;  
635 - }  
636 -  
637 - if ((ret = segment_open()) != ERROR_SUCCESS) {  
638 - srs_error("m3u8 muxer open segment failed. ret=%d", ret);  
639 - return ret;  
640 - }  
641 -  
642 - // TODO: flush audio before or after segment?  
643 - // segment open, flush the audio.  
644 - // @see: ngx_rtmp_hls_open_fragment  
645 - /* start fragment with audio to make iPhone happy */  
646 - if ((ret = flush_audio(af, ab)) != ERROR_SUCCESS) {  
647 - srs_error("m3u8 muxer flush audio failed. ret=%d", ret);  
648 - return ret;  
649 - }  
650 - }  
651 630
652 // update the duration of segment. 631 // update the duration of segment.
653 - current->update_duration(video_stream_dts); 632 + current->update_duration(vf->dts);
654 633
655 if ((ret = current->muxer->write_video(vf, vb)) != ERROR_SUCCESS) { 634 if ((ret = current->muxer->write_video(vf, vb)) != ERROR_SUCCESS) {
656 return ret; 635 return ret;
657 } 636 }
658 -  
659 - // write success, clear and free the buffer  
660 - vb->free();  
661 637
662 return ret; 638 return ret;
663 } 639 }
664 640
665 -int SrsM3u8Muxer::segment_close() 641 +int SrsHlsMuxer::segment_close()
666 { 642 {
667 int ret = ERROR_SUCCESS; 643 int ret = ERROR_SUCCESS;
668 644
@@ -675,7 +651,7 @@ int SrsM3u8Muxer::segment_close() @@ -675,7 +651,7 @@ int SrsM3u8Muxer::segment_close()
675 srs_assert(current); 651 srs_assert(current);
676 652
677 // assert segment duplicate. 653 // assert segment duplicate.
678 - std::vector<SrsM3u8Segment*>::iterator it; 654 + std::vector<SrsHlsSegment*>::iterator it;
679 it = std::find(segments.begin(), segments.end(), current); 655 it = std::find(segments.begin(), segments.end(), current);
680 srs_assert(it == segments.end()); 656 srs_assert(it == segments.end());
681 657
@@ -691,13 +667,13 @@ int SrsM3u8Muxer::segment_close() @@ -691,13 +667,13 @@ int SrsM3u8Muxer::segment_close()
691 current = NULL; 667 current = NULL;
692 668
693 // the segments to remove 669 // the segments to remove
694 - std::vector<SrsM3u8Segment*> segment_to_remove; 670 + std::vector<SrsHlsSegment*> segment_to_remove;
695 671
696 // shrink the segments. 672 // shrink the segments.
697 double duration = 0; 673 double duration = 0;
698 int remove_index = -1; 674 int remove_index = -1;
699 for (int i = segments.size() - 1; i >= 0; i--) { 675 for (int i = segments.size() - 1; i >= 0; i--) {
700 - SrsM3u8Segment* segment = segments[i]; 676 + SrsHlsSegment* segment = segments[i];
701 duration += segment->duration; 677 duration += segment->duration;
702 678
703 if ((int)duration > hls_window) { 679 if ((int)duration > hls_window) {
@@ -706,7 +682,7 @@ int SrsM3u8Muxer::segment_close() @@ -706,7 +682,7 @@ int SrsM3u8Muxer::segment_close()
706 } 682 }
707 } 683 }
708 for (int i = 0; i < remove_index && !segments.empty(); i++) { 684 for (int i = 0; i < remove_index && !segments.empty(); i++) {
709 - SrsM3u8Segment* segment = *segments.begin(); 685 + SrsHlsSegment* segment = *segments.begin();
710 segments.erase(segments.begin()); 686 segments.erase(segments.begin());
711 segment_to_remove.push_back(segment); 687 segment_to_remove.push_back(segment);
712 } 688 }
@@ -716,7 +692,7 @@ int SrsM3u8Muxer::segment_close() @@ -716,7 +692,7 @@ int SrsM3u8Muxer::segment_close()
716 692
717 // remove the ts file. 693 // remove the ts file.
718 for (int i = 0; i < (int)segment_to_remove.size(); i++) { 694 for (int i = 0; i < (int)segment_to_remove.size(); i++) {
719 - SrsM3u8Segment* segment = segment_to_remove[i]; 695 + SrsHlsSegment* segment = segment_to_remove[i];
720 unlink(segment->full_path.c_str()); 696 unlink(segment->full_path.c_str());
721 srs_freep(segment); 697 srs_freep(segment);
722 } 698 }
@@ -731,7 +707,7 @@ int SrsM3u8Muxer::segment_close() @@ -731,7 +707,7 @@ int SrsM3u8Muxer::segment_close()
731 return ret; 707 return ret;
732 } 708 }
733 709
734 -int SrsM3u8Muxer::refresh_m3u8() 710 +int SrsHlsMuxer::refresh_m3u8()
735 { 711 {
736 int ret = ERROR_SUCCESS; 712 int ret = ERROR_SUCCESS;
737 713
@@ -761,7 +737,7 @@ int SrsM3u8Muxer::refresh_m3u8() @@ -761,7 +737,7 @@ int SrsM3u8Muxer::refresh_m3u8()
761 return ret; 737 return ret;
762 } 738 }
763 739
764 -int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file) 740 +int SrsHlsMuxer::_refresh_m3u8(int& fd, std::string m3u8_file)
765 { 741 {
766 int ret = ERROR_SUCCESS; 742 int ret = ERROR_SUCCESS;
767 743
@@ -795,7 +771,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file) @@ -795,7 +771,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
795 srs_verbose("write m3u8 header success."); 771 srs_verbose("write m3u8 header success.");
796 772
797 // #EXT-X-MEDIA-SEQUENCE:4294967295\n 773 // #EXT-X-MEDIA-SEQUENCE:4294967295\n
798 - SrsM3u8Segment* first = *segments.begin(); 774 + SrsHlsSegment* first = *segments.begin();
799 char sequence[34] = {}; 775 char sequence[34] = {};
800 int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no); 776 int len = snprintf(sequence, sizeof(sequence), "#EXT-X-MEDIA-SEQUENCE:%d\n", first->sequence_no);
801 if (::write(fd, sequence, len) != len) { 777 if (::write(fd, sequence, len) != len) {
@@ -807,9 +783,9 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file) @@ -807,9 +783,9 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
807 783
808 // #EXT-X-TARGETDURATION:4294967295\n 784 // #EXT-X-TARGETDURATION:4294967295\n
809 int target_duration = 0; 785 int target_duration = 0;
810 - std::vector<SrsM3u8Segment*>::iterator it; 786 + std::vector<SrsHlsSegment*>::iterator it;
811 for (it = segments.begin(); it != segments.end(); ++it) { 787 for (it = segments.begin(); it != segments.end(); ++it) {
812 - SrsM3u8Segment* segment = *it; 788 + SrsHlsSegment* segment = *it;
813 target_duration = srs_max(target_duration, (int)segment->duration); 789 target_duration = srs_max(target_duration, (int)segment->duration);
814 } 790 }
815 // TODO: maybe need to take an around value 791 // TODO: maybe need to take an around value
@@ -825,7 +801,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file) @@ -825,7 +801,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
825 801
826 // write all segments 802 // write all segments
827 for (it = segments.begin(); it != segments.end(); ++it) { 803 for (it = segments.begin(); it != segments.end(); ++it) {
828 - SrsM3u8Segment* segment = *it; 804 + SrsHlsSegment* segment = *it;
829 805
830 // "#EXTINF:4294967295.208,\n" 806 // "#EXTINF:4294967295.208,\n"
831 char ext_info[25]; 807 char ext_info[25];
@@ -852,7 +828,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file) @@ -852,7 +828,7 @@ int SrsM3u8Muxer::_refresh_m3u8(int& fd, std::string m3u8_file)
852 return ret; 828 return ret;
853 } 829 }
854 830
855 -int SrsM3u8Muxer::create_dir() 831 +int SrsHlsMuxer::create_dir()
856 { 832 {
857 int ret = ERROR_SUCCESS; 833 int ret = ERROR_SUCCESS;
858 834
@@ -875,7 +851,7 @@ int SrsM3u8Muxer::create_dir() @@ -875,7 +851,7 @@ int SrsM3u8Muxer::create_dir()
875 return ret; 851 return ret;
876 } 852 }
877 853
878 -SrsTSCache::SrsTSCache() 854 +SrsHlsCache::SrsHlsCache()
879 { 855 {
880 aac_jitter = new SrsHlsAacJitter(); 856 aac_jitter = new SrsHlsAacJitter();
881 857
@@ -886,7 +862,7 @@ SrsTSCache::SrsTSCache() @@ -886,7 +862,7 @@ SrsTSCache::SrsTSCache()
886 vf = new SrsMpegtsFrame(); 862 vf = new SrsMpegtsFrame();
887 } 863 }
888 864
889 -SrsTSCache::~SrsTSCache() 865 +SrsHlsCache::~SrsHlsCache()
890 { 866 {
891 srs_freep(aac_jitter); 867 srs_freep(aac_jitter);
892 868
@@ -899,8 +875,52 @@ SrsTSCache::~SrsTSCache() @@ -899,8 +875,52 @@ SrsTSCache::~SrsTSCache()
899 srs_freep(af); 875 srs_freep(af);
900 srs_freep(vf); 876 srs_freep(vf);
901 } 877 }
  878 +
  879 +int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment_start_dts)
  880 +{
  881 + int ret = ERROR_SUCCESS;
  882 +
  883 + std::string vhost = req->vhost;
  884 + std::string stream = req->stream;
  885 + std::string app = req->app;
902 886
903 -int SrsTSCache::write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, SrsCodecSample* sample) 887 + int hls_fragment = _srs_config->get_hls_fragment(vhost);
  888 + int hls_window = _srs_config->get_hls_window(vhost);
  889 +
  890 + // get the hls path config
  891 + std::string hls_path = _srs_config->get_hls_path(vhost);
  892 +
  893 + // open muxer
  894 + if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) {
  895 + srs_error("m3u8 muxer update config failed. ret=%d", ret);
  896 + return ret;
  897 + }
  898 +
  899 + if ((ret = muxer->segment_open(segment_start_dts)) != ERROR_SUCCESS) {
  900 + srs_error("m3u8 muxer open segment failed. ret=%d", ret);
  901 + return ret;
  902 + }
  903 +
  904 + return ret;
  905 +}
  906 +
  907 +int SrsHlsCache::on_unpublish(SrsHlsMuxer* muxer)
  908 +{
  909 + int ret = ERROR_SUCCESS;
  910 +
  911 + if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) {
  912 + srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
  913 + return ret;
  914 + }
  915 +
  916 + if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
  917 + return ret;
  918 + }
  919 +
  920 + return ret;
  921 +}
  922 +
  923 +int SrsHlsCache::write_audio(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t pts, SrsCodecSample* sample)
904 { 924 {
905 int ret = ERROR_SUCCESS; 925 int ret = ERROR_SUCCESS;
906 926
@@ -939,7 +959,7 @@ int SrsTSCache::write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, S @@ -939,7 +959,7 @@ int SrsTSCache::write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, S
939 return ret; 959 return ret;
940 } 960 }
941 961
942 -int SrsTSCache::write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, SrsCodecSample* sample) 962 +int SrsHlsCache::write_video(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t dts, SrsCodecSample* sample)
943 { 963 {
944 int ret = ERROR_SUCCESS; 964 int ret = ERROR_SUCCESS;
945 965
@@ -954,28 +974,43 @@ int SrsTSCache::write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, S @@ -954,28 +974,43 @@ int SrsTSCache::write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, S
954 vf->sid = TS_VIDEO_AVC; 974 vf->sid = TS_VIDEO_AVC;
955 vf->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; 975 vf->key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
956 976
  977 + // reopen the muxer for a gop
  978 + // close current segment, open a new segment,
  979 + // then write the key frame to the new segment.
  980 + if (vf->key && muxer->is_segment_overflow()) {
  981 + if ((ret = muxer->segment_close()) != ERROR_SUCCESS) {
  982 + srs_error("m3u8 muxer close segment failed. ret=%d", ret);
  983 + return ret;
  984 + }
  985 +
  986 + if ((ret = muxer->segment_open(vf->dts)) != ERROR_SUCCESS) {
  987 + srs_error("m3u8 muxer open segment failed. ret=%d", ret);
  988 + return ret;
  989 + }
  990 +
  991 + // TODO: flush audio before or after segment?
  992 + // segment open, flush the audio.
  993 + // @see: ngx_rtmp_hls_open_fragment
  994 + /* start fragment with audio to make iPhone happy */
  995 + if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) {
  996 + srs_error("m3u8 muxer flush audio failed. ret=%d", ret);
  997 + return ret;
  998 + }
  999 + }
  1000 +
957 // flush video when got one 1001 // flush video when got one
958 if ((ret = muxer->flush_video(af, ab, vf, vb)) != ERROR_SUCCESS) { 1002 if ((ret = muxer->flush_video(af, ab, vf, vb)) != ERROR_SUCCESS) {
959 srs_error("m3u8 muxer flush video failed. ret=%d", ret); 1003 srs_error("m3u8 muxer flush video failed. ret=%d", ret);
960 return ret; 1004 return ret;
961 } 1005 }
962 1006
963 - return ret;  
964 -}  
965 -  
966 -int SrsTSCache::flush_audio(SrsM3u8Muxer* muxer)  
967 -{  
968 - int ret = ERROR_SUCCESS;  
969 -  
970 - if ((ret = muxer->flush_audio(af, ab)) != ERROR_SUCCESS) {  
971 - srs_error("m3u8 muxer flush audio failed. ret=%d", ret);  
972 - return ret;  
973 - } 1007 + // write success, clear and free the buffer
  1008 + vb->free();
974 1009
975 return ret; 1010 return ret;
976 } 1011 }
977 1012
978 -int SrsTSCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample) 1013 +int SrsHlsCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample)
979 { 1014 {
980 int ret = ERROR_SUCCESS; 1015 int ret = ERROR_SUCCESS;
981 1016
@@ -1042,7 +1077,7 @@ int SrsTSCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample) @@ -1042,7 +1077,7 @@ int SrsTSCache::cache_audio(SrsCodec* codec, SrsCodecSample* sample)
1042 return ret; 1077 return ret;
1043 } 1078 }
1044 1079
1045 -int SrsTSCache::cache_video(SrsCodec* codec, SrsCodecSample* sample) 1080 +int SrsHlsCache::cache_video(SrsCodec* codec, SrsCodecSample* sample)
1046 { 1081 {
1047 int ret = ERROR_SUCCESS; 1082 int ret = ERROR_SUCCESS;
1048 1083
@@ -1118,10 +1153,11 @@ SrsHls::SrsHls(SrsSource* _source) @@ -1118,10 +1153,11 @@ SrsHls::SrsHls(SrsSource* _source)
1118 sample = new SrsCodecSample(); 1153 sample = new SrsCodecSample();
1119 jitter = new SrsRtmpJitter(); 1154 jitter = new SrsRtmpJitter();
1120 1155
1121 - muxer = new SrsM3u8Muxer();  
1122 - ts_cache = new SrsTSCache(); 1156 + muxer = new SrsHlsMuxer();
  1157 + hls_cache = new SrsHlsCache();
1123 1158
1124 pithy_print = new SrsPithyPrint(SRS_STAGE_HLS); 1159 pithy_print = new SrsPithyPrint(SRS_STAGE_HLS);
  1160 + stream_dts = 0;
1125 } 1161 }
1126 1162
1127 SrsHls::~SrsHls() 1163 SrsHls::~SrsHls()
@@ -1131,7 +1167,7 @@ SrsHls::~SrsHls() @@ -1131,7 +1167,7 @@ SrsHls::~SrsHls()
1131 srs_freep(jitter); 1167 srs_freep(jitter);
1132 1168
1133 srs_freep(muxer); 1169 srs_freep(muxer);
1134 - srs_freep(ts_cache); 1170 + srs_freep(hls_cache);
1135 1171
1136 srs_freep(pithy_print); 1172 srs_freep(pithy_print);
1137 } 1173 }
@@ -1144,32 +1180,13 @@ int SrsHls::on_publish(SrsRequest* req) @@ -1144,32 +1180,13 @@ int SrsHls::on_publish(SrsRequest* req)
1144 if (hls_enabled) { 1180 if (hls_enabled) {
1145 return ret; 1181 return ret;
1146 } 1182 }
1147 -  
1148 - std::string vhost = req->vhost;  
1149 - std::string stream = req->stream;  
1150 - std::string app = req->app;  
1151 1183
  1184 + std::string vhost = req->vhost;
1152 if (!_srs_config->get_hls_enabled(vhost)) { 1185 if (!_srs_config->get_hls_enabled(vhost)) {
1153 return ret; 1186 return ret;
1154 } 1187 }
1155 1188
1156 - // if enabled, open the muxer.  
1157 - hls_enabled = true;  
1158 -  
1159 - int hls_fragment = _srs_config->get_hls_fragment(vhost);  
1160 - int hls_window = _srs_config->get_hls_window(vhost);  
1161 -  
1162 - // get the hls path config  
1163 - std::string hls_path = _srs_config->get_hls_path(vhost);  
1164 -  
1165 - // open muxer  
1166 - if ((ret = muxer->update_config(app, stream, hls_path, hls_fragment, hls_window)) != ERROR_SUCCESS) {  
1167 - srs_error("m3u8 muxer update config failed. ret=%d", ret);  
1168 - return ret;  
1169 - }  
1170 -  
1171 - if ((ret = muxer->segment_open()) != ERROR_SUCCESS) {  
1172 - srs_error("m3u8 muxer open segment failed. ret=%d", ret); 1189 + if ((ret = hls_cache->on_publish(muxer, req, stream_dts)) != ERROR_SUCCESS) {
1173 return ret; 1190 return ret;
1174 } 1191 }
1175 1192
@@ -1178,6 +1195,9 @@ int SrsHls::on_publish(SrsRequest* req) @@ -1178,6 +1195,9 @@ int SrsHls::on_publish(SrsRequest* req)
1178 srs_error("callback source hls start failed. ret=%d", ret); 1195 srs_error("callback source hls start failed. ret=%d", ret);
1179 return ret; 1196 return ret;
1180 } 1197 }
  1198 +
  1199 + // if enabled, open the muxer.
  1200 + hls_enabled = true;
1181 1201
1182 return ret; 1202 return ret;
1183 } 1203 }
@@ -1190,12 +1210,8 @@ void SrsHls::on_unpublish() @@ -1190,12 +1210,8 @@ void SrsHls::on_unpublish()
1190 if (!hls_enabled) { 1210 if (!hls_enabled) {
1191 return; 1211 return;
1192 } 1212 }
1193 -  
1194 - // close muxer when unpublish.  
1195 - ret = ts_cache->flush_audio(muxer);  
1196 - ret += muxer->segment_close();  
1197 1213
1198 - if (ret != ERROR_SUCCESS) { 1214 + if ((ret = hls_cache->on_unpublish(muxer)) != ERROR_SUCCESS) {
1199 srs_error("ignore m3u8 muxer flush/close audio failed. ret=%d", ret); 1215 srs_error("ignore m3u8 muxer flush/close audio failed. ret=%d", ret);
1200 } 1216 }
1201 1217
@@ -1287,8 +1303,11 @@ int SrsHls::on_audio(SrsSharedPtrMessage* audio) @@ -1287,8 +1303,11 @@ int SrsHls::on_audio(SrsSharedPtrMessage* audio)
1287 // the pts calc from rtmp/flv header. 1303 // the pts calc from rtmp/flv header.
1288 int64_t pts = audio->header.timestamp * 90; 1304 int64_t pts = audio->header.timestamp * 90;
1289 1305
1290 - if ((ret = ts_cache->write_audio(codec, muxer, pts, sample)) != ERROR_SUCCESS) {  
1291 - srs_error("ts cache write audio failed. ret=%d", ret); 1306 + // for pure audio, we need to update the stream dts also.
  1307 + stream_dts = pts;
  1308 +
  1309 + if ((ret = hls_cache->write_audio(codec, muxer, pts, sample)) != ERROR_SUCCESS) {
  1310 + srs_error("hls cache write audio failed. ret=%d", ret);
1292 return ret; 1311 return ret;
1293 } 1312 }
1294 1313
@@ -1327,8 +1346,9 @@ int SrsHls::on_video(SrsSharedPtrMessage* video) @@ -1327,8 +1346,9 @@ int SrsHls::on_video(SrsSharedPtrMessage* video)
1327 } 1346 }
1328 1347
1329 int64_t dts = video->header.timestamp * 90; 1348 int64_t dts = video->header.timestamp * 90;
1330 - if ((ret = ts_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) {  
1331 - srs_error("ts cache write video failed. ret=%d", ret); 1349 + stream_dts = dts;
  1350 + if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) {
  1351 + srs_error("hls cache write video failed. ret=%d", ret);
1332 return ret; 1352 return ret;
1333 } 1353 }
1334 1354
@@ -76,7 +76,10 @@ public: @@ -76,7 +76,10 @@ public:
76 virtual void on_buffer_continue(); 76 virtual void on_buffer_continue();
77 }; 77 };
78 78
79 -//TODO: refine the ts muxer, do more jobs. 79 +/**
  80 +* write data from frame(header info) and buffer(data) to ts file.
  81 +* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter
  82 +*/
80 class SrsTSMuxer 83 class SrsTSMuxer
81 { 84 {
82 private: 85 private:
@@ -93,11 +96,14 @@ public: @@ -93,11 +96,14 @@ public:
93 }; 96 };
94 97
95 /** 98 /**
  99 +* the wrapper of m3u8 segment from specification:
  100 +*
96 * 3.3.2. EXTINF 101 * 3.3.2. EXTINF
97 * The EXTINF tag specifies the duration of a media segment. 102 * The EXTINF tag specifies the duration of a media segment.
98 */ 103 */
99 -struct SrsM3u8Segment 104 +class SrsHlsSegment
100 { 105 {
  106 +public:
101 // duration in seconds in m3u8. 107 // duration in seconds in m3u8.
102 double duration; 108 double duration;
103 // sequence number in m3u8. 109 // sequence number in m3u8.
@@ -111,19 +117,25 @@ struct SrsM3u8Segment @@ -111,19 +117,25 @@ struct SrsM3u8Segment
111 // current segment start dts for m3u8 117 // current segment start dts for m3u8
112 int64_t segment_start_dts; 118 int64_t segment_start_dts;
113 119
114 - SrsM3u8Segment();  
115 - virtual ~SrsM3u8Segment(); 120 + SrsHlsSegment();
  121 + virtual ~SrsHlsSegment();
116 122
117 /** 123 /**
118 * update the segment duration. 124 * update the segment duration.
  125 + * @current_frame_dts the dts of frame, in tbn of ts.
119 */ 126 */
120 - virtual double update_duration(int64_t video_stream_dts); 127 + virtual double update_duration(int64_t current_frame_dts);
121 }; 128 };
122 129
123 /** 130 /**
124 -* muxer the m3u8 and ts files. 131 +* muxer the HLS stream(m3u8 and ts files).
  132 +* generally, the m3u8 muxer only provides methods to open/close segments,
  133 +* to flush video/audio, without any mechenisms.
  134 +*
  135 +* that is, user must use HlsCache, which will control the methods of muxer,
  136 +* and provides HLS mechenisms.
125 */ 137 */
126 -class SrsM3u8Muxer 138 +class SrsHlsMuxer
127 { 139 {
128 private: 140 private:
129 std::string app; 141 std::string app;
@@ -137,21 +149,37 @@ private: @@ -137,21 +149,37 @@ private:
137 std::string m3u8; 149 std::string m3u8;
138 private: 150 private:
139 /** 151 /**
  152 + * for pure audio HLS application,
  153 + * the video count used to count the video,
  154 + * if zero and audio buffer overflow, reap the ts,
  155 + * just like we got a keyframe.
  156 + */
  157 + u_int32_t video_count;
  158 +private:
  159 + /**
140 * m3u8 segments. 160 * m3u8 segments.
141 */ 161 */
142 - std::vector<SrsM3u8Segment*> segments; 162 + std::vector<SrsHlsSegment*> segments;
143 /** 163 /**
144 * current writing segment. 164 * current writing segment.
145 */ 165 */
146 - SrsM3u8Segment* current;  
147 - // last known dts  
148 - int64_t video_stream_dts; 166 + SrsHlsSegment* current;
149 public: 167 public:
150 - SrsM3u8Muxer();  
151 - virtual ~SrsM3u8Muxer(); 168 + SrsHlsMuxer();
  169 + virtual ~SrsHlsMuxer();
152 public: 170 public:
153 virtual int update_config(std::string _app, std::string _stream, std::string path, int fragment, int window); 171 virtual int update_config(std::string _app, std::string _stream, std::string path, int fragment, int window);
154 - virtual int segment_open(); 172 + /**
  173 + * open a new segment(a new ts file),
  174 + * @param segment_start_dts use to calc the segment duration,
  175 + * use 0 for the first segment of HLS.
  176 + */
  177 + virtual int segment_open(int64_t segment_start_dts);
  178 + /**
  179 + * whether video overflow,
  180 + * that is whether the current segment duration >= the segment in config
  181 + */
  182 + virtual bool is_segment_overflow();
155 virtual int flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab); 183 virtual int flush_audio(SrsMpegtsFrame* af, SrsCodecBuffer* ab);
156 virtual int flush_video(SrsMpegtsFrame* af, SrsCodecBuffer* ab, SrsMpegtsFrame* vf, SrsCodecBuffer* vb); 184 virtual int flush_video(SrsMpegtsFrame* af, SrsCodecBuffer* ab, SrsMpegtsFrame* vf, SrsCodecBuffer* vb);
157 virtual int segment_close(); 185 virtual int segment_close();
@@ -162,9 +190,23 @@ private: @@ -162,9 +190,23 @@ private:
162 }; 190 };
163 191
164 /** 192 /**
165 -* ts need to cache some audio then flush 193 +* hls stream cache,
  194 +* use to cache hls stream and flush to hls muxer.
  195 +*
  196 +* when write stream to ts file:
  197 +* video frame will directly flush to M3u8Muxer,
  198 +* audio frame need to cache, because it's small and flv tbn problem.
  199 +*
  200 +* whatever, the Hls cache used to cache video/audio,
  201 +* and flush video/audio to m3u8 muxer if needed.
  202 +*
  203 +* about the flv tbn problem:
  204 +* flv tbn is 1/1000, ts tbn is 1/90000,
  205 +* when timestamp convert to flv tbn, it will loose precise,
  206 +* so we must gather audio frame together, and recalc the timestamp @see SrsHlsAacJitter,
  207 +* we use a aac jitter to correct the audio pts.
166 */ 208 */
167 -class SrsTSCache 209 +class SrsHlsCache
168 { 210 {
169 private: 211 private:
170 // current frame and buffer 212 // current frame and buffer
@@ -178,34 +220,37 @@ private: @@ -178,34 +220,37 @@ private:
178 // time jitter for aac 220 // time jitter for aac
179 SrsHlsAacJitter* aac_jitter; 221 SrsHlsAacJitter* aac_jitter;
180 public: 222 public:
181 - SrsTSCache();  
182 - virtual ~SrsTSCache(); 223 + SrsHlsCache();
  224 + virtual ~SrsHlsCache();
183 public: 225 public:
184 /** 226 /**
185 - * write audio to cache, if need to flush, flush to muxer. 227 + * when publish or unpublish stream.
186 */ 228 */
187 - virtual int write_audio(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t pts, SrsCodecSample* sample); 229 + virtual int on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment_start_dts);
  230 + virtual int on_unpublish(SrsHlsMuxer* muxer);
188 /** 231 /**
189 - * write video to muxer. 232 + * write audio to cache, if need to flush, flush to muxer.
190 */ 233 */
191 - virtual int write_video(SrsCodec* codec, SrsM3u8Muxer* muxer, int64_t dts, SrsCodecSample* sample); 234 + virtual int write_audio(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t pts, SrsCodecSample* sample);
192 /** 235 /**
193 - * flush audio in cache to muxer. 236 + * write video to muxer.
194 */ 237 */
195 - virtual int flush_audio(SrsM3u8Muxer* muxer); 238 + virtual int write_video(SrsCodec* codec, SrsHlsMuxer* muxer, int64_t dts, SrsCodecSample* sample);
196 private: 239 private:
197 virtual int cache_audio(SrsCodec* codec, SrsCodecSample* sample); 240 virtual int cache_audio(SrsCodec* codec, SrsCodecSample* sample);
198 virtual int cache_video(SrsCodec* codec, SrsCodecSample* sample); 241 virtual int cache_video(SrsCodec* codec, SrsCodecSample* sample);
199 }; 242 };
200 243
201 /** 244 /**
202 -* write m3u8 hls. 245 +* delivery RTMP stream to HLS(m3u8 and ts),
  246 +* SrsHls provides interface with SrsSource.
  247 +*
203 */ 248 */
204 class SrsHls 249 class SrsHls
205 { 250 {
206 private: 251 private:
207 - SrsM3u8Muxer* muxer;  
208 - SrsTSCache* ts_cache; 252 + SrsHlsMuxer* muxer;
  253 + SrsHlsCache* hls_cache;
209 private: 254 private:
210 bool hls_enabled; 255 bool hls_enabled;
211 SrsSource* source; 256 SrsSource* source;
@@ -213,6 +258,20 @@ private: @@ -213,6 +258,20 @@ private:
213 SrsCodecSample* sample; 258 SrsCodecSample* sample;
214 SrsRtmpJitter* jitter; 259 SrsRtmpJitter* jitter;
215 SrsPithyPrint* pithy_print; 260 SrsPithyPrint* pithy_print;
  261 + /**
  262 + * we store the stream dts,
  263 + * for when we notice the hls cache to publish,
  264 + * it need to know the segment start dts.
  265 + *
  266 + * for example. when republish, the stream dts will
  267 + * monotonically increase, and the ts dts should start
  268 + * from current dts.
  269 + *
  270 + * or, simply because the HlsCache never free when unpublish,
  271 + * so when publish or republish it must start at stream dts,
  272 + * not zero dts.
  273 + */
  274 + int64_t stream_dts;
216 public: 275 public:
217 SrsHls(SrsSource* _source); 276 SrsHls(SrsSource* _source);
218 virtual ~SrsHls(); 277 virtual ~SrsHls();