refine the hls module, clear logic dead code, rename TSCache to HlsCache, M3u8Mu…
…xer to HlsMuxer. that is, make it to more readable.
正在显示
3 个修改的文件
包含
241 行增加
和
162 行删除
| @@ -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(); |
-
请 注册 或 登录 后发表评论