Skip to content

Commit

Permalink
Implement stream ordering in HLS playlist based on input order.
Browse files Browse the repository at this point in the history
Fixed #560 with suggested implementation:
#560 (comment)
  • Loading branch information
wjywbs committed Oct 17, 2023
1 parent 8465f5f commit 4e520c5
Show file tree
Hide file tree
Showing 19 changed files with 132 additions and 82 deletions.
1 change: 1 addition & 0 deletions packager/app/packager_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ int PackagerMain(int argc, char** argv) {
ParseStreamDescriptor(argv[i]);
if (!stream_descriptor)
return kArgumentValidationFailed;
stream_descriptor->output_order = i;
stream_descriptors.push_back(stream_descriptor.value());
}
Packager packager;
Expand Down
1 change: 1 addition & 0 deletions packager/hls/base/hls_notifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class HlsNotifier {
const std::string& playlist_name,
const std::string& stream_name,
const std::string& group_id,
int output_order,
uint32_t* stream_id) = 0;

/// Change the sample duration of stream with @a stream_id.
Expand Down
42 changes: 30 additions & 12 deletions packager/hls/base/master_playlist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,17 @@ struct Variant {
uint64_t avg_audio_bitrate = 0;
};

uint64_t GetMaximumMaxBitrate(const std::list<const MediaPlaylist*> playlists) {
uint64_t GetMaximumMaxBitrate(
const std::vector<const MediaPlaylist*> playlists) {
uint64_t max = 0;
for (const auto& playlist : playlists) {
max = std::max(max, playlist->MaxBitrate());
}
return max;
}

uint64_t GetMaximumAvgBitrate(const std::list<const MediaPlaylist*> playlists) {
uint64_t GetMaximumAvgBitrate(
const std::vector<const MediaPlaylist*> playlists) {
uint64_t max = 0;
for (const auto& playlist : playlists) {
max = std::max(max, playlist->AvgBitrate());
Expand All @@ -69,7 +71,7 @@ uint64_t GetMaximumAvgBitrate(const std::list<const MediaPlaylist*> playlists) {
}

std::set<std::string> GetGroupCodecString(
const std::list<const MediaPlaylist*>& group) {
const std::vector<const MediaPlaylist*>& group) {
std::set<std::string> codecs;

for (const MediaPlaylist* playlist : group) {
Expand Down Expand Up @@ -97,7 +99,7 @@ std::set<std::string> GetGroupCodecString(
}

std::list<Variant> AudioGroupsToVariants(
const std::map<std::string, std::list<const MediaPlaylist*>>& groups) {
const std::map<std::string, std::vector<const MediaPlaylist*>>& groups) {
std::list<Variant> variants;

for (const auto& group : groups) {
Expand Down Expand Up @@ -139,7 +141,7 @@ const char* GetGroupId(const MediaPlaylist& playlist) {
}

std::list<Variant> SubtitleGroupsToVariants(
const std::map<std::string, std::list<const MediaPlaylist*>>& groups) {
const std::map<std::string, std::vector<const MediaPlaylist*>>& groups) {
std::list<Variant> variants;

for (const auto& group : groups) {
Expand All @@ -160,8 +162,9 @@ std::list<Variant> SubtitleGroupsToVariants(
}

std::list<Variant> BuildVariants(
const std::map<std::string, std::list<const MediaPlaylist*>>& audio_groups,
const std::map<std::string, std::list<const MediaPlaylist*>>&
const std::map<std::string, std::vector<const MediaPlaylist*>>&
audio_groups,
const std::map<std::string, std::vector<const MediaPlaylist*>>&
subtitle_groups) {
std::list<Variant> audio_variants = AudioGroupsToVariants(audio_groups);
std::list<Variant> subtitle_variants =
Expand Down Expand Up @@ -358,7 +361,7 @@ void BuildMediaTag(const MediaPlaylist& playlist,
}

void BuildMediaTags(
const std::map<std::string, std::list<const MediaPlaylist*>>& groups,
const std::map<std::string, std::vector<const MediaPlaylist*>>& groups,
const std::string& default_language,
const std::string& base_url,
std::string* out) {
Expand Down Expand Up @@ -410,11 +413,12 @@ void AppendPlaylists(const std::string& default_audio_language,
const std::string& base_url,
const std::list<MediaPlaylist*>& playlists,
std::string* content) {
std::map<std::string, std::list<const MediaPlaylist*>> audio_playlist_groups;
std::map<std::string, std::list<const MediaPlaylist*>>
std::map<std::string, std::vector<const MediaPlaylist*>>
audio_playlist_groups;
std::map<std::string, std::vector<const MediaPlaylist*>>
subtitle_playlist_groups;
std::list<const MediaPlaylist*> video_playlists;
std::list<const MediaPlaylist*> iframe_playlists;
std::vector<const MediaPlaylist*> video_playlists;
std::vector<const MediaPlaylist*> iframe_playlists;
for (const MediaPlaylist* playlist : playlists) {
switch (playlist->stream_type()) {
case MediaPlaylist::MediaPlaylistStreamType::kAudio:
Expand All @@ -435,6 +439,20 @@ void AppendPlaylists(const std::string& default_audio_language,
}
}

// Sort the streams by input order in the command line.
auto comparator = [](const MediaPlaylist* a, const MediaPlaylist* b) {
return a->output_order() < b->output_order();
};
for (auto& it : audio_playlist_groups) {
std::stable_sort(it.second.begin(), it.second.end(), comparator);
}
for (auto& it : subtitle_playlist_groups) {
std::stable_sort(it.second.begin(), it.second.end(), comparator);
}
std::stable_sort(video_playlists.begin(), video_playlists.end(), comparator);
std::stable_sort(iframe_playlists.begin(), iframe_playlists.end(),
comparator);

if (!audio_playlist_groups.empty()) {
content->append("\n");
BuildMediaTags(audio_playlist_groups, default_audio_language, base_url,
Expand Down
6 changes: 3 additions & 3 deletions packager/hls/base/master_playlist_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ std::unique_ptr<MockMediaPlaylist> CreateVideoPlaylist(
const char kNoGroup[] = "";

std::unique_ptr<MockMediaPlaylist> playlist(
new MockMediaPlaylist(filename, kNoName, kNoGroup));
new MockMediaPlaylist(filename, kNoName, kNoGroup, 0));

playlist->SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kVideo);
Expand Down Expand Up @@ -91,7 +91,7 @@ std::unique_ptr<MockMediaPlaylist> CreateAudioPlaylist(
bool ac4_ims_flag,
bool ac4_cbi_flag) {
std::unique_ptr<MockMediaPlaylist> playlist(
new MockMediaPlaylist(filename, name, group));
new MockMediaPlaylist(filename, name, group, 0));

EXPECT_CALL(*playlist, GetNumChannels()).WillRepeatedly(Return(channels));
EXPECT_CALL(*playlist, GetEC3JocComplexity())
Expand Down Expand Up @@ -123,7 +123,7 @@ std::unique_ptr<MockMediaPlaylist> CreateTextPlaylist(
const std::string& codec,
const std::string& language) {
std::unique_ptr<MockMediaPlaylist> playlist(
new MockMediaPlaylist(filename, name, group));
new MockMediaPlaylist(filename, name, group, 0));

playlist->SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kSubtitle);
Expand Down
12 changes: 7 additions & 5 deletions packager/hls/base/media_playlist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -339,16 +339,18 @@ HlsEntry::~HlsEntry() {}
MediaPlaylist::MediaPlaylist(const HlsParams& hls_params,
const std::string& file_name,
const std::string& name,
const std::string& group_id)
const std::string& group_id,
int output_order)
: hls_params_(hls_params),
file_name_(file_name),
name_(name),
group_id_(group_id),
output_order_(output_order),
media_sequence_number_(hls_params_.media_sequence_number) {
// When there's a forced media_sequence_number, start with discontinuity
if (media_sequence_number_ > 0)
entries_.emplace_back(new DiscontinuityEntry());
}
// When there's a forced media_sequence_number, start with discontinuity
if (media_sequence_number_ > 0)
entries_.emplace_back(new DiscontinuityEntry());
}

MediaPlaylist::~MediaPlaylist() {}

Expand Down
5 changes: 4 additions & 1 deletion packager/hls/base/media_playlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ class MediaPlaylist {
MediaPlaylist(const HlsParams& hls_params,
const std::string& file_name,
const std::string& name,
const std::string& group_id);
const std::string& group_id,
int output_order);
virtual ~MediaPlaylist();

const std::string& file_name() const { return file_name_; }
const std::string& name() const { return name_; }
const std::string& group_id() const { return group_id_; }
int output_order() const { return output_order_; }
MediaPlaylistStreamType stream_type() const { return stream_type_; }
const std::string& codec() const { return codec_; }

Expand Down Expand Up @@ -253,6 +255,7 @@ class MediaPlaylist {
const std::string file_name_;
const std::string name_;
const std::string group_id_;
int output_order_;
MediaInfo media_info_;
MediaPlaylistStreamType stream_type_ = MediaPlaylistStreamType::kUnknown;
// Whether to use byte range for SegmentInfoEntry.
Expand Down
4 changes: 2 additions & 2 deletions packager/hls/base/media_playlist_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class MediaPlaylistTest : public ::testing::Test {
default_group_id_("default_group_id") {
hls_params_.playlist_type = type;
hls_params_.time_shift_buffer_depth = kTimeShiftBufferDepth;
media_playlist_.reset(new MediaPlaylist(hls_params_, default_file_name_,
default_name_, default_group_id_));
media_playlist_.reset(new MediaPlaylist(
hls_params_, default_file_name_, default_name_, default_group_id_, 0));
}

void SetUp() override {
Expand Down
5 changes: 3 additions & 2 deletions packager/hls/base/mock_media_playlist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ namespace hls {

MockMediaPlaylist::MockMediaPlaylist(const std::string& file_name,
const std::string& name,
const std::string& group_id)
: MediaPlaylist(HlsParams(), file_name, name, group_id) {}
const std::string& group_id,
int output_order)
: MediaPlaylist(HlsParams(), file_name, name, group_id, output_order) {}
MockMediaPlaylist::~MockMediaPlaylist() {}

} // namespace hls
Expand Down
3 changes: 2 additions & 1 deletion packager/hls/base/mock_media_playlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class MockMediaPlaylist : public MediaPlaylist {
// matter because the return value can be mocked.
MockMediaPlaylist(const std::string& file_name,
const std::string& name,
const std::string& group_id);
const std::string& group_id,
int output_order);
~MockMediaPlaylist() override;

MOCK_METHOD1(SetMediaInfo, bool(const MediaInfo& media_info));
Expand Down
8 changes: 5 additions & 3 deletions packager/hls/base/simple_hls_notifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,10 @@ std::unique_ptr<MediaPlaylist> MediaPlaylistFactory::Create(
const HlsParams& hls_params,
const std::string& file_name,
const std::string& name,
const std::string& group_id) {
const std::string& group_id,
int output_order) {
return std::unique_ptr<MediaPlaylist>(
new MediaPlaylist(hls_params, file_name, name, group_id));
new MediaPlaylist(hls_params, file_name, name, group_id, output_order));
}

SimpleHlsNotifier::SimpleHlsNotifier(const HlsParams& hls_params)
Expand Down Expand Up @@ -304,6 +305,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
const std::string& playlist_name,
const std::string& name,
const std::string& group_id,
int output_order,
uint32_t* stream_id) {
DCHECK(stream_id);

Expand All @@ -312,7 +314,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,

std::unique_ptr<MediaPlaylist> media_playlist =
media_playlist_factory_->Create(hls_params(), relative_playlist_path,
name, group_id);
name, group_id, output_order);
MediaInfo adjusted_media_info = MakeMediaInfoPathsRelativeToPlaylist(
media_info, hls_params().base_url, master_playlist_dir_,
media_playlist->file_name());
Expand Down
4 changes: 3 additions & 1 deletion packager/hls/base/simple_hls_notifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class MediaPlaylistFactory {
virtual std::unique_ptr<MediaPlaylist> Create(const HlsParams& hls_params,
const std::string& file_name,
const std::string& name,
const std::string& group_id);
const std::string& group_id,
int output_order);
};

/// This is thread safe.
Expand All @@ -48,6 +49,7 @@ class SimpleHlsNotifier : public HlsNotifier {
const std::string& playlist_name,
const std::string& stream_name,
const std::string& group_id,
int output_order,
uint32_t* stream_id) override;
bool NotifySampleDuration(uint32_t stream_id,
int32_t sample_duration) override;
Expand Down
Loading

0 comments on commit 4e520c5

Please sign in to comment.