DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
fragmenter.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/formats/mp4/fragmenter.h"
8 
9 #include <limits>
10 
11 #include "packager/media/base/buffer_writer.h"
12 #include "packager/media/base/audio_stream_info.h"
13 #include "packager/media/base/media_sample.h"
14 #include "packager/media/formats/mp4/box_definitions.h"
15 
16 namespace shaka {
17 namespace media {
18 namespace mp4 {
19 
20 namespace {
21 const int64_t kInvalidTime = std::numeric_limits<int64_t>::max();
22 
23 uint64_t GetSeekPreroll(const StreamInfo& stream_info) {
24  if (stream_info.stream_type() != kStreamAudio) return 0;
25  const AudioStreamInfo& audio_stream_info =
26  static_cast<const AudioStreamInfo&>(stream_info);
27  return audio_stream_info.seek_preroll_ns();
28 }
29 } // namespace
30 
31 Fragmenter::Fragmenter(scoped_refptr<StreamInfo> info, TrackFragment* traf)
32  : traf_(traf),
33  seek_preroll_(GetSeekPreroll(*info)),
34  fragment_initialized_(false),
35  fragment_finalized_(false),
36  fragment_duration_(0),
37  presentation_start_time_(kInvalidTime),
38  earliest_presentation_time_(kInvalidTime),
39  first_sap_time_(kInvalidTime) {
40  DCHECK(traf);
41 }
42 
43 Fragmenter::~Fragmenter() {}
44 
45 Status Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
46  DCHECK(sample);
47  CHECK_GT(sample->duration(), 0);
48 
49  if (!fragment_initialized_) {
50  Status status = InitializeFragment(sample->dts());
51  if (!status.ok())
52  return status;
53  }
54 
55  if (sample->side_data_size() > 0)
56  LOG(WARNING) << "MP4 samples do not support side data. Side data ignored.";
57 
58  // Fill in sample parameters. It will be optimized later.
59  traf_->runs[0].sample_sizes.push_back(sample->data_size());
60  traf_->runs[0].sample_durations.push_back(sample->duration());
61  traf_->runs[0].sample_flags.push_back(
62  sample->is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask);
63 
64  data_->AppendArray(sample->data(), sample->data_size());
65  fragment_duration_ += sample->duration();
66 
67  int64_t pts = sample->pts();
68 
69  // Set |earliest_presentation_time_| to |pts| if |pts| is smaller or if it is
70  // not yet initialized (kInvalidTime > pts is always true).
71  if (earliest_presentation_time_ > pts)
72  earliest_presentation_time_ = pts;
73 
74  traf_->runs[0].sample_composition_time_offsets.push_back(pts - sample->dts());
75  if (pts != sample->dts())
76  traf_->runs[0].flags |= TrackFragmentRun::kSampleCompTimeOffsetsPresentMask;
77 
78  if (sample->is_key_frame()) {
79  if (first_sap_time_ == kInvalidTime)
80  first_sap_time_ = pts;
81  }
82  return Status::OK;
83 }
84 
85 Status Fragmenter::InitializeFragment(int64_t first_sample_dts) {
86  fragment_initialized_ = true;
87  fragment_finalized_ = false;
88  traf_->decode_time.decode_time = first_sample_dts;
89  traf_->runs.clear();
90  traf_->runs.resize(1);
91  traf_->runs[0].flags = TrackFragmentRun::kDataOffsetPresentMask;
92  traf_->sample_group_descriptions.clear();
93  traf_->sample_to_groups.clear();
94  traf_->header.sample_description_index = 1; // 1-based.
95  traf_->header.flags = TrackFragmentHeader::kDefaultBaseIsMoofMask |
96  TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
97  fragment_duration_ = 0;
98  earliest_presentation_time_ = kInvalidTime;
99  first_sap_time_ = kInvalidTime;
100  data_.reset(new BufferWriter());
101  return Status::OK;
102 }
103 
105  // Optimize trun box.
106  traf_->runs[0].sample_count = traf_->runs[0].sample_sizes.size();
107  if (OptimizeSampleEntries(&traf_->runs[0].sample_durations,
108  &traf_->header.default_sample_duration)) {
109  traf_->header.flags |=
110  TrackFragmentHeader::kDefaultSampleDurationPresentMask;
111  } else {
112  traf_->runs[0].flags |= TrackFragmentRun::kSampleDurationPresentMask;
113  }
114  if (OptimizeSampleEntries(&traf_->runs[0].sample_sizes,
115  &traf_->header.default_sample_size)) {
116  traf_->header.flags |= TrackFragmentHeader::kDefaultSampleSizePresentMask;
117  } else {
118  traf_->runs[0].flags |= TrackFragmentRun::kSampleSizePresentMask;
119  }
120  if (OptimizeSampleEntries(&traf_->runs[0].sample_flags,
121  &traf_->header.default_sample_flags)) {
122  traf_->header.flags |= TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
123  } else {
124  traf_->runs[0].flags |= TrackFragmentRun::kSampleFlagsPresentMask;
125  }
126 
127  // Add SampleToGroup boxes. A SampleToGroup box with grouping type of 'roll'
128  // needs to be added if there is seek preroll, referencing sample group
129  // description in track level; Also need to add SampleToGroup boxes
130  // correponding to every SampleGroupDescription boxes, referencing sample
131  // group description in fragment level.
132  DCHECK_EQ(traf_->sample_to_groups.size(), 0u);
133  if (seek_preroll_ > 0) {
134  traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
135  SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
136  sample_to_group.grouping_type = FOURCC_roll;
137 
138  sample_to_group.entries.resize(1);
139  SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
140  sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
141  sample_to_group_entry.group_description_index =
142  SampleToGroupEntry::kTrackGroupDescriptionIndexBase + 1;
143  }
144  for (const auto& sample_group_description : traf_->sample_group_descriptions) {
145  traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
146  SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
147  sample_to_group.grouping_type = sample_group_description.grouping_type;
148 
149  sample_to_group.entries.resize(1);
150  SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
151  sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
152  sample_to_group_entry.group_description_index =
153  SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
154  }
155 
156  fragment_finalized_ = true;
157  fragment_initialized_ = false;
158 }
159 
161  // NOTE: Daisy chain is not supported currently.
162  reference->reference_type = false;
163  reference->subsegment_duration = fragment_duration_;
164  reference->starts_with_sap = StartsWithSAP();
165  if (kInvalidTime == first_sap_time_) {
166  reference->sap_type = SegmentReference::TypeUnknown;
167  reference->sap_delta_time = 0;
168  } else {
169  reference->sap_type = SegmentReference::Type1;
170  reference->sap_delta_time = first_sap_time_ - earliest_presentation_time_;
171  }
172  reference->earliest_presentation_time = earliest_presentation_time_;
173 }
174 
175 bool Fragmenter::StartsWithSAP() {
176  DCHECK(!traf_->runs.empty());
177  uint32_t start_sample_flag;
178  if (traf_->runs[0].flags & TrackFragmentRun::kSampleFlagsPresentMask) {
179  DCHECK(!traf_->runs[0].sample_flags.empty());
180  start_sample_flag = traf_->runs[0].sample_flags[0];
181  } else {
182  DCHECK(traf_->header.flags &
183  TrackFragmentHeader::kDefaultSampleFlagsPresentMask);
184  start_sample_flag = traf_->header.default_sample_flags;
185  }
186  return (start_sample_flag & TrackFragmentHeader::kNonKeySampleMask) == 0;
187 }
188 
189 } // namespace mp4
190 } // namespace media
191 } // namespace shaka
Fragmenter(scoped_refptr< StreamInfo > info, TrackFragment *traf)
Definition: fragmenter.cc:31
virtual Status AddSample(scoped_refptr< MediaSample > sample)
Definition: fragmenter.cc:45
virtual Status InitializeFragment(int64_t first_sample_dts)
Definition: fragmenter.cc:85
virtual void FinalizeFragment()
Finalize and optimize the fragment.
Definition: fragmenter.cc:104
void GenerateSegmentReference(SegmentReference *reference)
Fill reference with current fragment information.
Definition: fragmenter.cc:160
bool OptimizeSampleEntries(std::vector< T > *entries, T *default_value)
Definition: fragmenter.h:92