2022-08-26 15:44:59 +00:00
|
|
|
|
// Copyright 2016 Google LLC. All rights reserved.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
//
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
|
|
|
|
|
#ifndef PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|
|
|
|
|
#define PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|
|
|
|
|
|
2023-08-22 01:41:18 +00:00
|
|
|
|
#include <filesystem>
|
2016-03-25 08:35:44 +00:00
|
|
|
|
#include <list>
|
2016-08-30 23:01:19 +00:00
|
|
|
|
#include <memory>
|
2016-03-25 08:35:44 +00:00
|
|
|
|
#include <string>
|
2018-10-10 22:30:28 +00:00
|
|
|
|
#include <vector>
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2023-10-10 23:51:11 +00:00
|
|
|
|
#include <packager/hls/public/hls_params.h>
|
|
|
|
|
#include <packager/macros.h>
|
|
|
|
|
#include <packager/mpd/base/bandwidth_estimator.h>
|
|
|
|
|
#include <packager/mpd/base/media_info.pb.h>
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
|
namespace shaka {
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
|
|
|
|
class File;
|
|
|
|
|
|
|
|
|
|
namespace hls {
|
|
|
|
|
|
|
|
|
|
class HlsEntry {
|
|
|
|
|
public:
|
|
|
|
|
enum class EntryType {
|
|
|
|
|
kExtInf,
|
|
|
|
|
kExtKey,
|
2017-06-20 23:30:03 +00:00
|
|
|
|
kExtDiscontinuity,
|
2018-01-12 01:33:37 +00:00
|
|
|
|
kExtPlacementOpportunity,
|
2016-03-25 08:35:44 +00:00
|
|
|
|
};
|
|
|
|
|
virtual ~HlsEntry();
|
|
|
|
|
|
|
|
|
|
EntryType type() const { return type_; }
|
|
|
|
|
virtual std::string ToString() = 0;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
explicit HlsEntry(EntryType type);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
EntryType type_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Methods are virtual for mocking.
|
|
|
|
|
class MediaPlaylist {
|
|
|
|
|
public:
|
2016-04-20 22:23:19 +00:00
|
|
|
|
enum class MediaPlaylistStreamType {
|
2018-01-30 03:17:21 +00:00
|
|
|
|
kUnknown,
|
|
|
|
|
kAudio,
|
|
|
|
|
kVideo,
|
|
|
|
|
kVideoIFramesOnly,
|
|
|
|
|
kSubtitle,
|
2016-03-25 08:35:44 +00:00
|
|
|
|
};
|
|
|
|
|
enum class EncryptionMethod {
|
2017-04-04 20:57:50 +00:00
|
|
|
|
kNone, // No encryption, i.e. clear.
|
|
|
|
|
kAes128, // Completely encrypted using AES-CBC.
|
|
|
|
|
kSampleAes, // Encrypted using SAMPLE-AES method.
|
|
|
|
|
kSampleAesCenc, // 'cenc' encrypted content.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
};
|
|
|
|
|
|
2018-04-14 01:26:02 +00:00
|
|
|
|
/// @param hls_params contains HLS parameters.
|
2019-04-30 22:29:37 +00:00
|
|
|
|
/// @param file_name is the file name of this media playlist, relative to
|
|
|
|
|
/// master playlist output path.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// @param name is the name of this playlist. In other words this is the
|
|
|
|
|
/// value of the NAME attribute for EXT-X-MEDIA. This is not
|
|
|
|
|
/// necessarily the same as @a file_name.
|
|
|
|
|
/// @param group_id is the group ID for this playlist. This is the value of
|
|
|
|
|
/// GROUP-ID attribute for EXT-X-MEDIA.
|
2018-04-14 01:26:02 +00:00
|
|
|
|
MediaPlaylist(const HlsParams& hls_params,
|
2016-04-20 22:23:19 +00:00
|
|
|
|
const std::string& file_name,
|
|
|
|
|
const std::string& name,
|
|
|
|
|
const std::string& group_id);
|
2016-03-25 08:35:44 +00:00
|
|
|
|
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_; }
|
2016-04-20 22:23:19 +00:00
|
|
|
|
MediaPlaylistStreamType stream_type() const { return stream_type_; }
|
2016-03-25 08:35:44 +00:00
|
|
|
|
const std::string& codec() const { return codec_; }
|
|
|
|
|
|
|
|
|
|
/// For testing only.
|
2016-04-20 22:23:19 +00:00
|
|
|
|
void SetStreamTypeForTesting(MediaPlaylistStreamType stream_type);
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
|
|
|
|
/// For testing only.
|
|
|
|
|
void SetCodecForTesting(const std::string& codec);
|
|
|
|
|
|
2018-04-05 01:27:57 +00:00
|
|
|
|
/// For testing only.
|
|
|
|
|
void SetLanguageForTesting(const std::string& language);
|
|
|
|
|
|
2018-10-10 22:30:28 +00:00
|
|
|
|
/// For testing only.
|
|
|
|
|
void SetCharacteristicsForTesting(
|
|
|
|
|
const std::vector<std::string>& characteristics);
|
|
|
|
|
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// This must succeed before calling any other public methods.
|
|
|
|
|
/// @param media_info is the info of the segments that are going to be added
|
|
|
|
|
/// to this playlist.
|
|
|
|
|
/// @return true on success, false otherwise.
|
|
|
|
|
virtual bool SetMediaInfo(const MediaInfo& media_info);
|
|
|
|
|
|
2019-09-23 06:47:07 +00:00
|
|
|
|
/// Set the sample duration. Sample duration is used to generate frame rate.
|
|
|
|
|
/// Sample duration is not available right away especially. This allows
|
|
|
|
|
/// setting the sample duration after the Media Playlist has been initialized.
|
|
|
|
|
/// @param sample_duration is the duration of a sample.
|
2021-08-04 18:56:44 +00:00
|
|
|
|
virtual void SetSampleDuration(int32_t sample_duration);
|
2019-09-23 06:47:07 +00:00
|
|
|
|
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// Segments must be added in order.
|
|
|
|
|
/// @param file_name is the file name of the segment.
|
2017-06-03 00:05:47 +00:00
|
|
|
|
/// @param start_time is in terms of the timescale of the media.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// @param duration is in terms of the timescale of the media.
|
2017-05-01 20:38:58 +00:00
|
|
|
|
/// @param start_byte_offset is the offset of where the subsegment starts.
|
|
|
|
|
/// This must be 0 if the whole segment is a subsegment.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// @param size is size in bytes.
|
|
|
|
|
virtual void AddSegment(const std::string& file_name,
|
2018-06-22 01:14:34 +00:00
|
|
|
|
int64_t start_time,
|
|
|
|
|
int64_t duration,
|
2017-05-01 20:38:58 +00:00
|
|
|
|
uint64_t start_byte_offset,
|
2016-03-25 08:35:44 +00:00
|
|
|
|
uint64_t size);
|
|
|
|
|
|
2018-01-30 03:17:21 +00:00
|
|
|
|
/// Keyframes must be added in order. It is also called before the containing
|
|
|
|
|
/// segment being called.
|
|
|
|
|
/// @param timestamp is the timestamp of the key frame in timescale of the
|
|
|
|
|
/// media.
|
|
|
|
|
/// @param start_byte_offset is the offset of where the key frame starts.
|
|
|
|
|
/// @param size is size in bytes.
|
2018-06-22 01:14:34 +00:00
|
|
|
|
virtual void AddKeyFrame(int64_t timestamp,
|
2018-01-30 03:17:21 +00:00
|
|
|
|
uint64_t start_byte_offset,
|
|
|
|
|
uint64_t size);
|
|
|
|
|
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// All segments added after calling this method must be decryptable with
|
|
|
|
|
/// the key that can be fetched from |url|, until calling this again.
|
|
|
|
|
/// @param method is the encryption method.
|
|
|
|
|
/// @param url specifies where the key is i.e. the value of the URI attribute.
|
2017-04-04 20:57:50 +00:00
|
|
|
|
/// #param key_id is the default key ID for the encrypted segements.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// @param iv is the initialization vector in human readable format, i.e. the
|
|
|
|
|
/// value for IV attribute. This may be empty.
|
|
|
|
|
/// @param key_format is the key format, i.e. the KEYFORMAT value. This may be
|
|
|
|
|
/// empty.
|
|
|
|
|
/// @param key_format_versions is the KEYFORMATVERIONS value. This may be
|
|
|
|
|
/// empty.
|
|
|
|
|
virtual void AddEncryptionInfo(EncryptionMethod method,
|
|
|
|
|
const std::string& url,
|
2017-04-04 20:57:50 +00:00
|
|
|
|
const std::string& key_id,
|
2016-03-25 08:35:44 +00:00
|
|
|
|
const std::string& iv,
|
|
|
|
|
const std::string& key_format,
|
|
|
|
|
const std::string& key_format_versions);
|
|
|
|
|
|
2018-01-12 01:33:37 +00:00
|
|
|
|
/// Add #EXT-X-PLACEMENT-OPPORTUNITY for mid-roll ads. See
|
|
|
|
|
/// https://support.google.com/dfp_premium/answer/7295798?hl=en.
|
|
|
|
|
virtual void AddPlacementOpportunity();
|
|
|
|
|
|
2017-06-03 00:05:47 +00:00
|
|
|
|
/// Write the playlist to |file_path|.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// This does not close the file.
|
2020-07-04 21:55:28 +00:00
|
|
|
|
/// If target duration is not set explicitly, this will try to find the target
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// duration. Note that target duration cannot be changed. So calling this
|
|
|
|
|
/// without explicitly setting the target duration and before adding any
|
|
|
|
|
/// segments will end up setting the target duration to 0 and will always
|
|
|
|
|
/// generate an invalid playlist.
|
2017-06-03 00:05:47 +00:00
|
|
|
|
/// @param file_path is the output file path accepted by the File
|
|
|
|
|
/// implementation.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// @return true on success, false otherwise.
|
2023-08-22 01:41:18 +00:00
|
|
|
|
virtual bool WriteToFile(const std::filesystem::path& file_path);
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
|
|
|
|
/// If bitrate is specified in MediaInfo then it will use that value.
|
2016-06-30 03:09:36 +00:00
|
|
|
|
/// Otherwise, returns the max bitrate.
|
2018-05-31 21:27:21 +00:00
|
|
|
|
/// @return the max bitrate (in bits per second) of this MediaPlaylist.
|
|
|
|
|
virtual uint64_t MaxBitrate() const;
|
|
|
|
|
|
|
|
|
|
/// Unlike @a MaxBitrate, AvgBitrate is always computed from the segment size
|
|
|
|
|
/// and duration.
|
|
|
|
|
/// @return The average bitrate (in bits per second) of this MediaPlaylist.
|
|
|
|
|
virtual uint64_t AvgBitrate() const;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
|
|
|
|
/// @return the longest segment’s duration. This will return 0 if no
|
|
|
|
|
/// segments have been added.
|
2016-03-25 08:39:07 +00:00
|
|
|
|
virtual double GetLongestSegmentDuration() const;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
|
|
|
|
/// Set the target duration of this MediaPlaylist.
|
|
|
|
|
/// In other words this is the value for EXT-X-TARGETDURATION.
|
|
|
|
|
/// If this is not called before calling Write(), it will estimate the best
|
|
|
|
|
/// target duration.
|
2017-06-03 00:05:47 +00:00
|
|
|
|
/// The spec does not allow changing EXT-X-TARGETDURATION. However, this class
|
|
|
|
|
/// has no control over the input source.
|
2016-03-25 08:35:44 +00:00
|
|
|
|
/// @param target_duration is the target duration for this playlist.
|
2021-08-04 18:56:44 +00:00
|
|
|
|
virtual void SetTargetDuration(int32_t target_duration);
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2017-12-09 01:53:17 +00:00
|
|
|
|
/// @return number of channels for audio. 0 is returned for video.
|
|
|
|
|
virtual int GetNumChannels() const;
|
|
|
|
|
|
2020-06-04 05:02:49 +00:00
|
|
|
|
/// @return Dolby Digital Plus JOC decoding complexity, ETSI TS 103 420 v1.2.1
|
|
|
|
|
/// Backwards-compatible object audio carriage using Enhanced AC-3
|
|
|
|
|
/// Standard C.3.2.3.
|
|
|
|
|
virtual int GetEC3JocComplexity() const;
|
|
|
|
|
|
2020-07-04 21:55:28 +00:00
|
|
|
|
/// @return true if it's an AC-4 IMS stream, based on Dolby AC-4 in MPEG-DASH
|
|
|
|
|
/// for Online Delivery Specification 2.5.3.
|
|
|
|
|
/// https://developer.dolby.com/tools-media/online-delivery-kits/dolby-ac-4/
|
|
|
|
|
virtual bool GetAC4ImsFlag() const;
|
|
|
|
|
|
|
|
|
|
/// @return true if it's an AC-4 CBI stream, based on ETSI TS 103 190-2
|
|
|
|
|
/// Digital Audio Compression (AC-4) Standard; Part 2: Immersive and
|
|
|
|
|
/// personalized audio 4.3.
|
|
|
|
|
virtual bool GetAC4CbiFlag() const;
|
|
|
|
|
|
2017-06-14 19:25:17 +00:00
|
|
|
|
/// @return true if |width| and |height| have been set with a valid
|
|
|
|
|
/// resolution values.
|
2017-07-13 20:25:36 +00:00
|
|
|
|
virtual bool GetDisplayResolution(uint32_t* width, uint32_t* height) const;
|
2017-06-14 19:25:17 +00:00
|
|
|
|
|
2019-09-23 06:24:33 +00:00
|
|
|
|
/// @return The video range of the stream.
|
|
|
|
|
virtual std::string GetVideoRange() const;
|
|
|
|
|
|
2019-09-23 06:47:07 +00:00
|
|
|
|
/// @return the frame rate.
|
|
|
|
|
virtual double GetFrameRate() const;
|
|
|
|
|
|
2018-04-05 01:27:57 +00:00
|
|
|
|
/// @return the language of the media, as an ISO language tag in its shortest
|
|
|
|
|
/// form. May be an empty string for video.
|
2018-10-10 22:30:28 +00:00
|
|
|
|
const std::string& language() const { return language_; }
|
|
|
|
|
|
|
|
|
|
const std::vector<std::string>& characteristics() const {
|
|
|
|
|
return characteristics_;
|
|
|
|
|
}
|
2018-04-05 01:27:57 +00:00
|
|
|
|
|
2020-11-04 20:56:05 +00:00
|
|
|
|
bool is_dvs() const {
|
|
|
|
|
// HLS Authoring Specification for Apple Devices
|
|
|
|
|
// https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices#overview
|
|
|
|
|
// Section 2.12.
|
|
|
|
|
const char DVS_CHARACTERISTICS[] = "public.accessibility.describes-video";
|
|
|
|
|
return characteristics_.size() == 1 &&
|
|
|
|
|
characteristics_[0] == DVS_CHARACTERISTICS;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-25 08:35:44 +00:00
|
|
|
|
private:
|
2018-01-30 03:17:21 +00:00
|
|
|
|
// Add a SegmentInfoEntry (#EXTINF).
|
|
|
|
|
void AddSegmentInfoEntry(const std::string& segment_file_name,
|
2018-06-22 01:14:34 +00:00
|
|
|
|
int64_t start_time,
|
|
|
|
|
int64_t duration,
|
2018-01-30 03:17:21 +00:00
|
|
|
|
uint64_t start_byte_offset,
|
|
|
|
|
uint64_t size);
|
2018-05-16 17:56:22 +00:00
|
|
|
|
// Adjust the duration of the last SegmentInfoEntry to end on
|
|
|
|
|
// |next_timestamp|.
|
2018-06-22 01:14:34 +00:00
|
|
|
|
void AdjustLastSegmentInfoEntryDuration(int64_t next_timestamp);
|
2017-06-03 00:05:47 +00:00
|
|
|
|
// Remove elements from |entries_| for live profile. Increments
|
|
|
|
|
// |sequence_number_| by the number of segments removed.
|
|
|
|
|
void SlideWindow();
|
2018-04-17 22:03:02 +00:00
|
|
|
|
// Remove the segment specified by |start_time|. The actual deletion can
|
|
|
|
|
// happen at a later time depending on the value of
|
|
|
|
|
// |preserved_segment_outside_live_window| in |hls_params_|.
|
2018-06-22 01:14:34 +00:00
|
|
|
|
void RemoveOldSegment(int64_t start_time);
|
2017-06-03 00:05:47 +00:00
|
|
|
|
|
2018-04-17 22:03:02 +00:00
|
|
|
|
const HlsParams& hls_params_;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
// Mainly for MasterPlaylist to use these values.
|
|
|
|
|
const std::string file_name_;
|
|
|
|
|
const std::string name_;
|
|
|
|
|
const std::string group_id_;
|
|
|
|
|
MediaInfo media_info_;
|
2018-01-30 03:17:21 +00:00
|
|
|
|
MediaPlaylistStreamType stream_type_ = MediaPlaylistStreamType::kUnknown;
|
|
|
|
|
// Whether to use byte range for SegmentInfoEntry.
|
|
|
|
|
bool use_byte_range_ = false;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
std::string codec_;
|
2018-04-05 01:27:57 +00:00
|
|
|
|
std::string language_;
|
2018-10-10 22:30:28 +00:00
|
|
|
|
std::vector<std::string> characteristics_;
|
2020-01-31 18:25:19 +00:00
|
|
|
|
uint32_t media_sequence_number_ = 0;
|
2017-06-20 23:30:03 +00:00
|
|
|
|
bool inserted_discontinuity_tag_ = false;
|
2017-06-03 00:05:47 +00:00
|
|
|
|
int discontinuity_sequence_number_ = 0;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2019-08-07 23:14:45 +00:00
|
|
|
|
double longest_segment_duration_seconds_ = 0.0;
|
2021-08-04 18:56:44 +00:00
|
|
|
|
int32_t time_scale_ = 0;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2018-05-31 01:28:16 +00:00
|
|
|
|
BandwidthEstimator bandwidth_estimator_;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2017-05-01 20:38:58 +00:00
|
|
|
|
// Cache the previous calls AddSegment() end offset. This is used to construct
|
|
|
|
|
// SegmentInfoEntry.
|
|
|
|
|
uint64_t previous_segment_end_offset_ = 0;
|
|
|
|
|
|
2016-03-25 08:35:44 +00:00
|
|
|
|
// See SetTargetDuration() comments.
|
|
|
|
|
bool target_duration_set_ = false;
|
2021-08-04 18:56:44 +00:00
|
|
|
|
int32_t target_duration_ = 0;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2019-03-22 00:12:30 +00:00
|
|
|
|
// TODO(kqyang): This could be managed better by a separate class, than having
|
|
|
|
|
// all them managed in MediaPlaylist.
|
2016-08-30 23:01:19 +00:00
|
|
|
|
std::list<std::unique_ptr<HlsEntry>> entries_;
|
2019-03-22 00:12:30 +00:00
|
|
|
|
double current_buffer_depth_ = 0;
|
2018-04-17 22:03:02 +00:00
|
|
|
|
// A list to hold the file names of the segments to be removed temporarily.
|
|
|
|
|
// Once a file is actually removed, it is removed from the list.
|
|
|
|
|
std::list<std::string> segments_to_be_removed_;
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
2018-01-30 03:17:21 +00:00
|
|
|
|
// Used by kVideoIFrameOnly playlists to track the i-frames (key frames).
|
|
|
|
|
struct KeyFrameInfo {
|
2018-06-22 01:14:34 +00:00
|
|
|
|
int64_t timestamp;
|
2018-01-30 03:17:21 +00:00
|
|
|
|
uint64_t start_byte_offset;
|
|
|
|
|
uint64_t size;
|
|
|
|
|
std::string segment_file_name;
|
|
|
|
|
};
|
|
|
|
|
std::list<KeyFrameInfo> key_frames_;
|
|
|
|
|
|
2016-03-25 08:35:44 +00:00
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(MediaPlaylist);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace hls
|
2016-05-20 21:19:33 +00:00
|
|
|
|
} // namespace shaka
|
2016-03-25 08:35:44 +00:00
|
|
|
|
|
|
|
|
|
#endif // PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|