7 #include "packager/media/chunking/chunking_handler.h"
11 #include "packager/base/logging.h"
12 #include "packager/media/base/media_sample.h"
13 #include "packager/status_macros.h"
18 const size_t kStreamIndex = 0;
20 bool IsNewSegmentIndex(int64_t new_index, int64_t current_index) {
21 return new_index != current_index &&
26 new_index != current_index - 1;
31 ChunkingHandler::ChunkingHandler(
const ChunkingParams& chunking_params)
32 : chunking_params_(chunking_params) {
33 CHECK_NE(chunking_params.segment_duration_in_seconds, 0u);
36 Status ChunkingHandler::InitializeInternal() {
37 if (num_input_streams() != 1 || next_output_stream_index() != 1) {
38 return Status(error::INVALID_ARGUMENT,
39 "Expects exactly one input and one output.");
44 Status ChunkingHandler::Process(std::unique_ptr<StreamData> stream_data) {
45 switch (stream_data->stream_data_type) {
46 case StreamDataType::kStreamInfo:
47 return OnStreamInfo(std::move(stream_data->stream_info));
48 case StreamDataType::kCueEvent:
49 return OnCueEvent(std::move(stream_data->cue_event));
50 case StreamDataType::kSegmentInfo:
51 VLOG(3) <<
"Droppping existing segment info.";
53 case StreamDataType::kMediaSample:
54 return OnMediaSample(std::move(stream_data->media_sample));
56 VLOG(3) <<
"Stream data type "
57 <<
static_cast<int>(stream_data->stream_data_type) <<
" ignored.";
58 return Dispatch(std::move(stream_data));
62 Status ChunkingHandler::OnFlushRequest(
size_t input_stream_index) {
63 RETURN_IF_ERROR(EndSegmentIfStarted());
64 return FlushDownstream(kStreamIndex);
67 Status ChunkingHandler::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
68 time_scale_ = info->time_scale();
70 chunking_params_.segment_duration_in_seconds * time_scale_;
71 subsegment_duration_ =
72 chunking_params_.subsegment_duration_in_seconds * time_scale_;
73 return DispatchStreamInfo(kStreamIndex, std::move(info));
76 Status ChunkingHandler::OnCueEvent(std::shared_ptr<const CueEvent> event) {
77 RETURN_IF_ERROR(EndSegmentIfStarted());
78 const double event_time_in_seconds =
event->time_in_seconds;
79 RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
82 segment_start_time_ = base::nullopt;
85 cue_offset_ = event_time_in_seconds * time_scale_;
89 Status ChunkingHandler::OnMediaSample(
90 std::shared_ptr<const MediaSample> sample) {
91 DCHECK_NE(time_scale_, 0u) <<
"kStreamInfo should arrive before kMediaSample";
93 const int64_t timestamp = sample->pts();
95 bool started_new_segment =
false;
96 const bool can_start_new_segment =
97 sample->is_key_frame() || !chunking_params_.segment_sap_aligned;
98 if (can_start_new_segment) {
99 const int64_t segment_index =
100 timestamp < cue_offset_ ? 0
101 : (timestamp - cue_offset_) / segment_duration_;
102 if (!segment_start_time_ ||
103 IsNewSegmentIndex(segment_index, current_segment_index_)) {
104 current_segment_index_ = segment_index;
106 current_subsegment_index_ = 0;
108 RETURN_IF_ERROR(EndSegmentIfStarted());
109 segment_start_time_ = timestamp;
110 subsegment_start_time_ = timestamp;
111 max_segment_time_ = timestamp + sample->duration();
112 started_new_segment =
true;
115 if (!started_new_segment && IsSubsegmentEnabled()) {
116 const bool can_start_new_subsegment =
117 sample->is_key_frame() || !chunking_params_.subsegment_sap_aligned;
118 if (can_start_new_subsegment) {
119 const int64_t subsegment_index =
120 (timestamp - segment_start_time_.value()) / subsegment_duration_;
121 if (IsNewSegmentIndex(subsegment_index, current_subsegment_index_)) {
122 current_subsegment_index_ = subsegment_index;
124 RETURN_IF_ERROR(EndSubsegmentIfStarted());
125 subsegment_start_time_ = timestamp;
130 VLOG(3) <<
"Sample ts: " << timestamp <<
" "
131 <<
" duration: " << sample->duration() <<
" scale: " << time_scale_
132 << (segment_start_time_ ?
" dispatch " :
" discard ");
133 if (!segment_start_time_) {
134 DCHECK(!subsegment_start_time_);
140 segment_start_time_ = std::min(segment_start_time_.value(), timestamp);
141 subsegment_start_time_ = std::min(subsegment_start_time_.value(), timestamp);
143 std::max(max_segment_time_, timestamp + sample->duration());
144 return DispatchMediaSample(kStreamIndex, std::move(sample));
147 Status ChunkingHandler::EndSegmentIfStarted()
const {
148 if (!segment_start_time_)
151 auto segment_info = std::make_shared<SegmentInfo>();
152 segment_info->start_timestamp = segment_start_time_.value();
153 segment_info->duration = max_segment_time_ - segment_start_time_.value();
154 return DispatchSegmentInfo(kStreamIndex, std::move(segment_info));
157 Status ChunkingHandler::EndSubsegmentIfStarted()
const {
158 if (!subsegment_start_time_)
161 auto subsegment_info = std::make_shared<SegmentInfo>();
162 subsegment_info->start_timestamp = subsegment_start_time_.value();
163 subsegment_info->duration =
164 max_segment_time_ - subsegment_start_time_.value();
165 subsegment_info->is_subsegment =
true;
166 return DispatchSegmentInfo(kStreamIndex, std::move(subsegment_info));
All the methods that are virtual are virtual for mocking.