Skip to content

Commit 32a85e6

Browse files
committed
fix #4594: auto-only hls fmp4 play error.
based on @HeeJoon-Kim's patch. Root Cause: the last dts of the fmp4 segment incorrect. Additional: follows more mp4 standards.
1 parent a83c902 commit 32a85e6

File tree

4 files changed

+83
-31
lines changed

4 files changed

+83
-31
lines changed

trunk/src/app/srs_app_hls.cpp

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ srs_error_t SrsInitMp4Segment::init_encoder()
200200
SrsHlsM4sSegment::SrsHlsM4sSegment(ISrsFileWriter *fw)
201201
{
202202
fw_ = fw;
203+
sequence_no_ = 0;
203204
}
204205

205206
SrsHlsM4sSegment::~SrsHlsM4sSegment()
@@ -425,10 +426,10 @@ SrsHlsFmp4Muxer::SrsHlsFmp4Muxer()
425426
segments_ = new SrsFragmentWindow();
426427
latest_acodec_ = SrsAudioCodecIdForbidden;
427428
latest_vcodec_ = SrsVideoCodecIdForbidden;
429+
sequence_header_ = false;
428430
video_track_id_ = 0;
429431
audio_track_id_ = 0;
430432
init_mp4_ready_ = false;
431-
video_dts_ = 0;
432433

433434
memset(key_, 0, 16);
434435
memset(iv_, 0, 16);
@@ -645,8 +646,16 @@ srs_error_t SrsHlsFmp4Muxer::write_audio(SrsMediaPacket *shared_audio, SrsFormat
645646
}
646647
}
647648

648-
if (current_->duration() >= hls_fragment_) {
649-
if ((err = segment_close()) != srs_success) {
649+
// For pure audio, we use a larger threshold to reap segment.
650+
bool pure_audio_stream = (latest_vcodec_ == SrsVideoCodecIdForbidden || latest_vcodec_ == SrsVideoCodecIdDisabled);
651+
652+
bool reap = false;
653+
if (pure_audio_stream) {
654+
reap = is_segment_absolutely_overflow();
655+
}
656+
657+
if (reap) {
658+
if ((err = segment_close(shared_audio->timestamp_)) != srs_success) {
650659
return srs_error_wrap(err, "segment close");
651660
}
652661

@@ -663,17 +672,22 @@ srs_error_t SrsHlsFmp4Muxer::write_video(SrsMediaPacket *shared_video, SrsFormat
663672
{
664673
srs_error_t err = srs_success;
665674

666-
video_dts_ = shared_video->timestamp_;
667-
668675
if (!current_) {
669676
if ((err = segment_open(shared_video->timestamp_ * SRS_UTIME_MILLISECONDS)) != srs_success) {
670677
return srs_error_wrap(err, "open segment");
671678
}
672679
}
673680

674-
bool reopen = current_->duration() >= hls_fragment_;
681+
bool reopen = false;
682+
if (is_segment_overflow()) {
683+
// wait for keyframe to reap segment.
684+
if (!wait_keyframe() || format->video_->frame_type_ == SrsVideoAvcFrameTypeKeyFrame) {
685+
reopen = true;
686+
}
687+
}
688+
675689
if (reopen) {
676-
if ((err = segment_close()) != srs_success) {
690+
if ((err = segment_close(shared_video->timestamp_)) != srs_success) {
677691
return srs_error_wrap(err, "segment close");
678692
}
679693

@@ -785,6 +799,11 @@ srs_error_t SrsHlsFmp4Muxer::segment_open(srs_utime_t basetime)
785799
current_ = new SrsHlsM4sSegment(writer_);
786800
current_->sequence_no_ = sequence_no_++;
787801

802+
if (sequence_header_) {
803+
current_->set_sequence_header(true);
804+
sequence_header_ = false;
805+
}
806+
788807
if ((err = write_hls_key()) != srs_success) {
789808
return srs_error_wrap(err, "write hls key");
790809
}
@@ -882,6 +901,7 @@ bool SrsHlsFmp4Muxer::is_segment_open()
882901

883902
srs_error_t SrsHlsFmp4Muxer::on_sequence_header()
884903
{
904+
sequence_header_ = true;
885905
return srs_success;
886906
}
887907

@@ -936,14 +956,14 @@ void SrsHlsFmp4Muxer::update_duration(uint64_t dts)
936956
current_->append(dts / 90);
937957
}
938958

939-
srs_error_t SrsHlsFmp4Muxer::segment_close()
959+
srs_error_t SrsHlsFmp4Muxer::segment_close(uint64_t dts)
940960
{
941-
srs_error_t err = do_segment_close();
961+
srs_error_t err = do_segment_close(dts);
942962

943963
return err;
944964
}
945965

946-
srs_error_t SrsHlsFmp4Muxer::do_segment_close()
966+
srs_error_t SrsHlsFmp4Muxer::do_segment_close(uint64_t dts)
947967
{
948968
srs_error_t err = srs_success;
949969

@@ -952,7 +972,8 @@ srs_error_t SrsHlsFmp4Muxer::do_segment_close()
952972
return err;
953973
}
954974

955-
if ((err = current_->reap(video_dts_)) != srs_success) {
975+
if ((err = current_->reap(dts)) != srs_success) {
976+
956977
return srs_error_wrap(err, "reap segment");
957978
}
958979

@@ -2503,8 +2524,9 @@ srs_error_t SrsHlsMp4Controller::on_unpublish()
25032524
srs_error_t err = srs_success;
25042525
req_ = NULL;
25052526

2506-
if ((err = muxer_->segment_close()) != srs_success) {
2507-
return srs_error_wrap(err, "hls: segment close");
2527+
uint64_t last_dts = srs_max(audio_dts_, video_dts_);
2528+
if ((err = muxer_->segment_close(last_dts)) != srs_success) {
2529+
return srs_error_wrap(err, "hls: segment close");
25082530
}
25092531

25102532
if ((err = muxer_->on_unpublish()) != srs_success) {
@@ -2586,6 +2608,10 @@ srs_error_t SrsHlsMp4Controller::on_sequence_header(SrsMediaPacket *msg, SrsForm
25862608
return srs_error_wrap(err, "write init mp4");
25872609
}
25882610

2611+
if ((err = muxer_->on_sequence_header()) != srs_success) {
2612+
return srs_error_wrap(err, "on sequence header");
2613+
}
2614+
25892615
return err;
25902616
}
25912617

@@ -2967,3 +2993,4 @@ void SrsHls::hls_show_mux_log()
29672993
srsu2msi(controller_->duration()), controller_->deviation());
29682994
}
29692995
// LCOV_EXCL_STOP
2996+

trunk/src/app/srs_app_hls.hpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,6 @@ class SrsHlsFmp4Muxer
480480
std::string init_mp4_uri_; // URI for init.mp4 in m3u8 playlist
481481
int video_track_id_;
482482
int audio_track_id_;
483-
uint64_t video_dts_;
484483

485484
// clang-format off
486485
SRS_DECLARE_PRIVATE: // clang-format on
@@ -489,6 +488,8 @@ class SrsHlsFmp4Muxer
489488
// The current writing segment.
490489
SrsHlsM4sSegment *current_;
491490

491+
bool sequence_header_;
492+
492493
// clang-format off
493494
SRS_DECLARE_PRIVATE: // clang-format on
494495
// Latest audio codec, parsed from stream.
@@ -559,13 +560,14 @@ class SrsHlsFmp4Muxer
559560
// When flushing video or audio, we update the duration. But, we should also update the
560561
// duration before closing the segment. Keep in mind that it's fine to update the duration
561562
// several times using the same dts timestamp.
562-
void update_duration(uint64_t dts);
563-
// Close segment(ts).
564-
virtual srs_error_t segment_close();
563+
virtual void update_duration(uint64_t dts);
564+
// Close segment.
565+
virtual srs_error_t segment_close(uint64_t dts);
566+
565567

566568
// clang-format off
567569
SRS_DECLARE_PRIVATE: // clang-format on
568-
virtual srs_error_t do_segment_close();
570+
virtual srs_error_t do_segment_close(uint64_t dts);
569571
virtual srs_error_t write_hls_key();
570572
virtual srs_error_t refresh_m3u8();
571573
virtual srs_error_t do_refresh_m3u8(std::string m3u8_file);

trunk/src/kernel/srs_kernel_mp4.cpp

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5793,18 +5793,38 @@ srs_error_t SrsMp4SampleManager::write(SrsMp4TrackFragmentBox *traf, uint64_t dt
57935793
SrsMp4TrackFragmentRunBox *trun = traf->trun();
57945794
trun->flags_ = SrsMp4TrunFlagsDataOffset | SrsMp4TrunFlagsSampleDuration | SrsMp4TrunFlagsSampleSize | SrsMp4TrunFlagsSampleFlag | SrsMp4TrunFlagsSampleCtsOffset;
57955795

5796-
SrsMp4Sample *previous = NULL;
5797-
5796+
// ISO_IEC_14496-12-base-format-2012.pdf, 8.8.8.1 page 57
5797+
// Because trun->flags_ has not include SrsMp4TrunFlagsFirstSample(0x000004),
5798+
// so trun->first_sample_flags_ is not present, and each SrsMp4TrunEntry has sample_flags_.
5799+
// ISO_IEC_14496-12-base-format-2012.pdf, 8.8.3.1 page 53 define the sample_flags_'s layout.
5800+
// int(32) sample_flags = {
5801+
// bit(4) reserved = 0;
5802+
// int(2) is_leading;
5803+
// int(2) sample_depends_on;
5804+
// int(2) sample_is_depends_on;
5805+
// int(2) sample_has_redundancy;
5806+
// bit(3) sample_padding_value;
5807+
// bit(1) sample_is_non_sync_sample;
5808+
// int(16) sample_degradation_priority;
5809+
// }
5810+
// ISO_IEC_14496-12-base-format-2012.pdf, 8.6.4.1, 8.6.4.3 page 41 define the sample_flags_'s values.
5811+
// sample_depends_on == 1: this sample does depend on others;
5812+
// sample_depends_on == 2: this sample does not depend on others;
57985813
vector<SrsMp4Sample *>::iterator it;
57995814
for (it = samples_.begin(); it != samples_.end(); ++it) {
58005815
SrsMp4Sample *sample = *it;
58015816
SrsMp4TrunEntry *entry = new SrsMp4TrunEntry(trun);
58025817

5803-
if (!previous) {
5804-
previous = sample;
5805-
entry->sample_flags_ = 0x02000000;
5818+
// For audio, all samples are sync samples.
5819+
if (sample->type_ == SrsFrameTypeVideo) {
5820+
if (sample->frame_type_ == SrsVideoAvcFrameTypeKeyFrame) {
5821+
entry->sample_flags_ = 0x02000000; // sample_is_depended_on=2, others depend on this.
5822+
} else {
5823+
entry->sample_flags_ = 0x01000000; // sample_is_depended_on=1, this depends on others.
5824+
}
58065825
} else {
5807-
entry->sample_flags_ = 0x01000000;
5826+
// For audio, all samples are sync samples.
5827+
entry->sample_flags_ = 0x02000000;
58085828
}
58095829

58105830
vector<SrsMp4Sample *>::iterator iter = (it + 1);
@@ -7053,10 +7073,10 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat *format, bool video, int tid)
70537073
// Write ftyp box.
70547074
if (true) {
70557075
SrsUniquePtr<SrsMp4FileTypeBox> ftyp(new SrsMp4FileTypeBox());
7056-
7057-
ftyp->major_brand_ = SrsMp4BoxBrandISO5;
7058-
ftyp->minor_version_ = 512;
7059-
ftyp->set_compatible_brands(SrsMp4BoxBrandISO6, SrsMp4BoxBrandMP41);
7076+
7077+
ftyp->major_brand_ = SrsMp4BoxBrandMP42;
7078+
ftyp->minor_version_ = 0;
7079+
ftyp->set_compatible_brands(SrsMp4BoxBrandISO6, SrsMp4BoxBrandMP41, SrsMp4BoxBrandCMAF);
70607080

70617081
if ((err = srs_mp4_write_box(writer_, ftyp.get())) != srs_success) {
70627082
return srs_error_wrap(err, "write ftyp");
@@ -7303,9 +7323,9 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat *format, int v_tid, int a_tid
73037323
if (true) {
73047324
SrsUniquePtr<SrsMp4FileTypeBox> ftyp(new SrsMp4FileTypeBox());
73057325

7306-
ftyp->major_brand_ = SrsMp4BoxBrandMP42; // SrsMp4BoxBrandISO5;
7307-
ftyp->minor_version_ = 512;
7308-
ftyp->set_compatible_brands(SrsMp4BoxBrandISO6, SrsMp4BoxBrandMP41);
7326+
ftyp->major_brand_ = SrsMp4BoxBrandMP42;
7327+
ftyp->minor_version_ = 0;
7328+
ftyp->set_compatible_brands(SrsMp4BoxBrandISO6, SrsMp4BoxBrandMP41, SrsMp4BoxBrandCMAF);
73097329

73107330
if ((err = srs_mp4_write_box(writer_, ftyp.get())) != srs_success) {
73117331
return srs_error_wrap(err, "write ftyp");
@@ -8056,3 +8076,4 @@ srs_error_t SrsFmp4SegmentEncoder::flush(uint64_t dts)
80568076

80578077
return err;
80588078
}
8079+

trunk/src/kernel/srs_kernel_mp4.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ enum SrsMp4BoxBrand {
158158
SrsMp4BoxBrandMSDH = 0x6d736468, // 'msdh'
159159
SrsMp4BoxBrandMSIX = 0x6d736978, // 'msix'
160160
SrsMp4BoxBrandHEV1 = 0x68657631, // 'hev1'
161+
SrsMp4BoxBrandCMAF = 0x636d6661, // 'cmaf'
161162
};
162163

163164
// The context to dump.
@@ -3114,3 +3115,4 @@ void srs_mp4_pfn_elem(T &elem, std::stringstream &ss, SrsMp4DumpContext /*dc*/)
31143115
// LCOV_EXCL_STOP
31153116

31163117
#endif
3118+

0 commit comments

Comments
 (0)