New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding support for hls EXT_X_PROGRAM_DATE_TIME #840
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -490,6 +490,11 @@ base::Optional<PackagingParams> GetPackagingParams() { | |
hls_params.default_text_language = FLAGS_default_text_language; | ||
hls_params.media_sequence_number = FLAGS_hls_media_sequence_number; | ||
|
||
if (FLAGS_hls_ext_x_program_date_time != INT32_MIN) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the reason not to use 0 as default? |
||
hls_params.add_ext_x_program_date_time = true; | ||
hls_params.packaging_time_offset_ms = FLAGS_hls_ext_x_program_date_time; | ||
} | ||
|
||
TestParams& test_params = packaging_params.test_params; | ||
test_params.dump_stream_info = FLAGS_dump_stream_info; | ||
test_params.inject_fake_clock = FLAGS_use_fake_clock_for_muxer; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,9 +12,11 @@ | |
#include <cmath> | ||
#include <memory> | ||
|
||
#include "packager/app/hls_flags.h" | ||
#include "packager/base/logging.h" | ||
#include "packager/base/strings/string_number_conversions.h" | ||
#include "packager/base/strings/stringprintf.h" | ||
#include "packager/base/time/time.h" | ||
#include "packager/file/file.h" | ||
#include "packager/hls/base/tag.h" | ||
#include "packager/media/base/language_utils.h" | ||
|
@@ -170,7 +172,8 @@ class SegmentInfoEntry : public HlsEntry { | |
bool use_byte_range, | ||
uint64_t start_byte_offset, | ||
uint64_t segment_file_size, | ||
uint64_t previous_segment_end_offset); | ||
uint64_t previous_segment_end_offset, | ||
const base::Time wall_time); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass by const reference to avoid copying, i.e. |
||
|
||
std::string ToString() override; | ||
int64_t start_time() const { return start_time_; } | ||
|
@@ -179,6 +182,10 @@ class SegmentInfoEntry : public HlsEntry { | |
duration_seconds_ = duration_seconds; | ||
} | ||
|
||
const base::Time wall_time() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return a const reference to avoid copying, i.e.
|
||
return wall_time_; | ||
} | ||
|
||
private: | ||
SegmentInfoEntry(const SegmentInfoEntry&) = delete; | ||
SegmentInfoEntry& operator=(const SegmentInfoEntry&) = delete; | ||
|
@@ -190,6 +197,7 @@ class SegmentInfoEntry : public HlsEntry { | |
const uint64_t start_byte_offset_; | ||
const uint64_t segment_file_size_; | ||
const uint64_t previous_segment_end_offset_; | ||
const base::Time wall_time_; | ||
}; | ||
|
||
SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name, | ||
|
@@ -198,15 +206,17 @@ SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name, | |
bool use_byte_range, | ||
uint64_t start_byte_offset, | ||
uint64_t segment_file_size, | ||
uint64_t previous_segment_end_offset) | ||
uint64_t previous_segment_end_offset, | ||
const base::Time wall_time) | ||
: HlsEntry(HlsEntry::EntryType::kExtInf), | ||
file_name_(file_name), | ||
start_time_(start_time), | ||
duration_seconds_(duration_seconds), | ||
use_byte_range_(use_byte_range), | ||
start_byte_offset_(start_byte_offset), | ||
segment_file_size_(segment_file_size), | ||
previous_segment_end_offset_(previous_segment_end_offset) {} | ||
previous_segment_end_offset_(previous_segment_end_offset), | ||
wall_time_(wall_time) {} | ||
|
||
std::string SegmentInfoEntry::ToString() { | ||
std::string result = base::StringPrintf("#EXTINF:%.3f,", duration_seconds_); | ||
|
@@ -408,7 +418,8 @@ void MediaPlaylist::AddSegment(const std::string& file_name, | |
int64_t start_time, | ||
int64_t duration, | ||
uint64_t start_byte_offset, | ||
uint64_t size) { | ||
uint64_t size, | ||
const base::Time reference_time) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since it only changes on discontinuity, it is probably a good idea to provide it in a separate function instead of in AddSegment. |
||
if (stream_type_ == MediaPlaylistStreamType::kVideoIFramesOnly) { | ||
if (key_frames_.empty()) | ||
return; | ||
|
@@ -423,13 +434,13 @@ void MediaPlaylist::AddSegment(const std::string& file_name, | |
: std::next(iter)->timestamp; | ||
AddSegmentInfoEntry(file_name, iter->timestamp, | ||
next_timestamp - iter->timestamp, | ||
iter->start_byte_offset, iter->size); | ||
iter->start_byte_offset, iter->size, reference_time); | ||
} | ||
key_frames_.clear(); | ||
return; | ||
} | ||
return AddSegmentInfoEntry(file_name, start_time, duration, start_byte_offset, | ||
size); | ||
size, reference_time); | ||
} | ||
|
||
void MediaPlaylist::AddKeyFrame(int64_t timestamp, | ||
|
@@ -477,8 +488,34 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) { | |
media_info_, target_duration_, hls_params_.playlist_type, stream_type_, | ||
media_sequence_number_, discontinuity_sequence_number_); | ||
|
||
for (const auto& entry : entries_) | ||
bool need_ext_date_time = hls_params_.add_ext_x_program_date_time; | ||
|
||
for (const auto& entry : entries_) { | ||
switch (entry->type()) { | ||
case HlsEntry::EntryType::kExtInf: | ||
if (need_ext_date_time) { | ||
SegmentInfoEntry* segment_info = | ||
reinterpret_cast<SegmentInfoEntry*>(entry.get()); | ||
base::Time::Exploded time; | ||
segment_info->wall_time().UTCExplode(&time); | ||
base::StringAppendF(&content, | ||
"#EXT-X-PROGRAM-DATE-TIME:" | ||
"%4d-%02d-%02dT%02d:%02d:%02d.%03dZ\n", | ||
time.year, time.month, time.day_of_month, | ||
time.hour, time.minute, time.second, | ||
time.millisecond); | ||
need_ext_date_time = false; | ||
} | ||
break; | ||
case HlsEntry::EntryType::kExtDiscontinuity: | ||
// we need to add a new program data time after a discontinuity | ||
need_ext_date_time = hls_params_.add_ext_x_program_date_time; | ||
break; | ||
default: | ||
break; | ||
Comment on lines
+495
to
+515
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indent by two extra characters. |
||
} | ||
base::StringAppendF(&content, "%s\n", entry->ToString().c_str()); | ||
} | ||
|
||
if (hls_params_.playlist_type == HlsPlaylistType::kVod) { | ||
content += "#EXT-X-ENDLIST\n"; | ||
|
@@ -581,14 +618,15 @@ void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name, | |
int64_t start_time, | ||
int64_t duration, | ||
uint64_t start_byte_offset, | ||
uint64_t size) { | ||
uint64_t size, | ||
base::Time reference_time) { | ||
if (time_scale_ == 0) { | ||
LOG(WARNING) << "Timescale is not set and the duration for " << duration | ||
<< " cannot be calculated. The output will be wrong."; | ||
|
||
entries_.emplace_back(new SegmentInfoEntry( | ||
segment_file_name, 0.0, 0.0, use_byte_range_, start_byte_offset, size, | ||
previous_segment_end_offset_)); | ||
previous_segment_end_offset_, reference_time)); | ||
return; | ||
} | ||
|
||
|
@@ -605,26 +643,27 @@ void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name, | |
std::max(longest_segment_duration_seconds_, segment_duration_seconds); | ||
bandwidth_estimator_.AddBlock(size, segment_duration_seconds); | ||
current_buffer_depth_ += segment_duration_seconds; | ||
|
||
if (!entries_.empty() && | ||
entries_.back()->type() == HlsEntry::EntryType::kExtInf) { | ||
const SegmentInfoEntry* segment_info = | ||
static_cast<SegmentInfoEntry*>(entries_.back().get()); | ||
if (segment_info->start_time() > start_time) { | ||
LOG(WARNING) | ||
<< "Insert a discontinuity tag after the segment with start time " | ||
<< segment_info->start_time() << " as the next segment starts at " | ||
<< start_time << "."; | ||
entries_.emplace_back(new DiscontinuityEntry()); | ||
} | ||
const base::Time wall_time = reference_time + base::TimeDelta::FromSeconds(start_time / time_scale_); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can defer the wall_time computation to |
||
|
||
if (!entries_.empty() && start_time < last_segment_start_time_) { | ||
LOG(WARNING) | ||
<< "Insert a discontinuity tag after the segment with start time " | ||
<< last_segment_start_time_ << " as the next segment starts at " | ||
<< start_time << "."; | ||
entries_.emplace_back(new DiscontinuityEntry()); | ||
} | ||
|
||
last_segment_start_time_ = start_time; | ||
entries_.emplace_back(new SegmentInfoEntry( | ||
segment_file_name, start_time, segment_duration_seconds, use_byte_range_, | ||
start_byte_offset, size, previous_segment_end_offset_)); | ||
start_byte_offset, size, previous_segment_end_offset_, wall_time)); | ||
previous_segment_end_offset_ = start_byte_offset + size - 1; | ||
} | ||
|
||
uint64_t MediaPlaylist::LastSegmentStartTime() const { | ||
return last_segment_start_time_; | ||
} | ||
|
||
void MediaPlaylist::AdjustLastSegmentInfoEntryDuration(int64_t next_timestamp) { | ||
if (time_scale_ == 0) | ||
return; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a definitive answer myself right now. I am thinking whether it is a good idea to reuse --suggested_presentation_delay from DASH for the offset in HLS instead having another offset here.