DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
ts_segmenter.cc
1 // Copyright 2016 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/mp2t/ts_segmenter.h"
8 
9 #include <memory>
10 
11 #include "packager/media/base/aes_encryptor.h"
12 #include "packager/media/base/key_source.h"
13 #include "packager/media/base/muxer_util.h"
14 #include "packager/media/base/status.h"
15 #include "packager/media/base/video_stream_info.h"
16 #include "packager/media/event/muxer_listener.h"
17 #include "packager/media/event/progress_listener.h"
18 
19 namespace shaka {
20 namespace media {
21 namespace mp2t {
22 
23 namespace {
24 const double kTsTimescale = 90000;
25 } // namespace
26 
28  : muxer_options_(options),
29  listener_(listener),
30  ts_writer_(new TsWriter()),
31  pes_packet_generator_(new PesPacketGenerator()) {}
32 TsSegmenter::~TsSegmenter() {}
33 
35  KeySource* encryption_key_source,
36  uint32_t max_sd_pixels,
37  double clear_lead_in_seconds) {
38  if (muxer_options_.segment_template.empty())
39  return Status(error::MUXER_FAILURE, "Segment template not specified.");
40  if (!ts_writer_->Initialize(stream_info))
41  return Status(error::MUXER_FAILURE, "Failed to initialize TsWriter.");
42  if (!pes_packet_generator_->Initialize(stream_info)) {
43  return Status(error::MUXER_FAILURE,
44  "Failed to initialize PesPacketGenerator.");
45  }
46 
47  if (encryption_key_source) {
48  scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
49  const KeySource::TrackType type =
50  GetTrackTypeForEncryption(stream_info, max_sd_pixels);
51  Status status = encryption_key_source->GetKey(type, encryption_key.get());
52 
53  if (encryption_key->iv.empty()) {
54  if (!AesCryptor::GenerateRandomIv(FOURCC_cbcs, &encryption_key->iv)) {
55  return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
56  }
57  }
58  if (!status.ok())
59  return status;
60 
61  encryption_key_ = encryption_key.Pass();
62  clear_lead_in_seconds_ = clear_lead_in_seconds;
63 
64  if (listener_) {
65  // For now this only happens once, so send true.
66  const bool kIsInitialEncryptionInfo = true;
67  listener_->OnEncryptionInfoReady(
68  kIsInitialEncryptionInfo, FOURCC_cbcs, encryption_key_->key_id,
69  encryption_key_->iv, encryption_key_->key_system_info);
70  }
71 
72  status = NotifyEncrypted();
73  if (!status.ok())
74  return status;
75  }
76 
77  timescale_scale_ = kTsTimescale / stream_info.time_scale();
78  return Status::OK;
79 }
80 
82  return Flush();
83 }
84 
85 // First checks whether the sample is a key frame. If so and the segment has
86 // passed the segment duration, then flush the generator and write all the data
87 // to file.
88 Status TsSegmenter::AddSample(scoped_refptr<MediaSample> sample) {
89  const bool passed_segment_duration =
90  current_segment_total_sample_duration_ > muxer_options_.segment_duration;
91  if (sample->is_key_frame() && passed_segment_duration) {
92  Status status = Flush();
93  if (!status.ok())
94  return status;
95  }
96 
97  if (!ts_writer_file_opened_ && !sample->is_key_frame())
98  LOG(WARNING) << "A segment will start with a non key frame.";
99 
100  if (!pes_packet_generator_->PushSample(sample)) {
101  return Status(error::MUXER_FAILURE,
102  "Failed to add sample to PesPacketGenerator.");
103  }
104 
105  const double scaled_sample_duration = sample->duration() * timescale_scale_;
106  current_segment_total_sample_duration_ +=
107  scaled_sample_duration / kTsTimescale;
108 
109  return WritePesPacketsToFile();
110 }
111 
112 void TsSegmenter::InjectTsWriterForTesting(scoped_ptr<TsWriter> writer) {
113  ts_writer_ = writer.Pass();
114 }
115 
117  scoped_ptr<PesPacketGenerator> generator) {
118  pes_packet_generator_ = generator.Pass();
119 }
120 
122  ts_writer_file_opened_ = value;
123 }
124 
125 Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) {
126  if (ts_writer_file_opened_)
127  return Status::OK;
128  const std::string segment_name =
129  GetSegmentName(muxer_options_.segment_template, next_pts,
130  segment_number_++, muxer_options_.bandwidth);
131  if (!ts_writer_->NewSegment(segment_name))
132  return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");
133  current_segment_start_time_ = next_pts;
134  current_segment_path_ = segment_name;
135  ts_writer_file_opened_ = true;
136  return Status::OK;
137 }
138 
139 Status TsSegmenter::WritePesPacketsToFile() {
140  while (pes_packet_generator_->NumberOfReadyPesPackets() > 0u) {
141  scoped_ptr<PesPacket> pes_packet =
142  pes_packet_generator_->GetNextPesPacket();
143 
144  Status status = OpenNewSegmentIfClosed(pes_packet->pts());
145  if (!status.ok())
146  return status;
147 
148  if (!ts_writer_->AddPesPacket(pes_packet.Pass()))
149  return Status(error::MUXER_FAILURE, "Failed to add PES packet.");
150  }
151  return Status::OK;
152 }
153 
154 Status TsSegmenter::Flush() {
155  if (!pes_packet_generator_->Flush()) {
156  return Status(error::MUXER_FAILURE,
157  "Failed to flush PesPacketGenerator.");
158  }
159  Status status = WritePesPacketsToFile();
160  if (!status.ok())
161  return status;
162 
163  // This method may be called from Finalize() so ts_writer_file_opened_ could
164  // be false.
165  if (ts_writer_file_opened_) {
166  if (!ts_writer_->FinalizeSegment()) {
167  return Status(error::MUXER_FAILURE, "Failed to finalize TsWriter.");
168  }
169  if (listener_) {
170  const int64_t file_size =
171  File::GetFileSize(current_segment_path_.c_str());
172  listener_->OnNewSegment(
173  current_segment_path_, current_segment_start_time_,
174  current_segment_total_sample_duration_ * kTsTimescale, file_size);
175  }
176  ts_writer_file_opened_ = false;
177  total_duration_in_seconds_ += current_segment_total_sample_duration_;
178  }
179  current_segment_total_sample_duration_ = 0.0;
180  current_segment_start_time_ = 0;
181  current_segment_path_.clear();
182  return NotifyEncrypted();
183 }
184 
185 Status TsSegmenter::NotifyEncrypted() {
186  if (encryption_key_ && total_duration_in_seconds_ >= clear_lead_in_seconds_) {
187  if (listener_)
188  listener_->OnEncryptionStart();
189 
190  if (!pes_packet_generator_->SetEncryptionKey(encryption_key_.Pass()))
191  return Status(error::INTERNAL_ERROR, "Failed to set encryption key.");
192  ts_writer_->SignalEncypted();
193  }
194  return Status::OK;
195 }
196 
197 } // namespace mp2t
198 } // namespace media
199 } // namespace shaka
virtual void OnNewSegment(const std::string &segment_name, uint64_t start_time, uint64_t duration, uint64_t segment_file_size)=0
virtual void OnEncryptionInfoReady(bool is_initial_encryption_info, FourCC protection_scheme, const std::vector< uint8_t > &key_id, const std::vector< uint8_t > &iv, const std::vector< ProtectionSystemSpecificInfo > &key_system_info)=0
Abstract class holds stream information.
Definition: stream_info.h:26
Status AddSample(scoped_refptr< MediaSample > sample)
Definition: ts_segmenter.cc:88
virtual void OnEncryptionStart()=0
This structure contains the list of configuration options for Muxer.
Definition: muxer_options.h:18
virtual Status GetKey(TrackType track_type, EncryptionKey *key)=0
void InjectTsWriterForTesting(scoped_ptr< TsWriter > writer)
Only for testing.
TsSegmenter(const MuxerOptions &options, MuxerListener *listener)
Definition: ts_segmenter.cc:27
static bool GenerateRandomIv(FourCC protection_scheme, std::vector< uint8_t > *iv)
Definition: aes_cryptor.cc:109
void SetTsWriterFileOpenedForTesting(bool value)
Only for testing.
void InjectPesPacketGeneratorForTesting(scoped_ptr< PesPacketGenerator > generator)
Only for testing.
static int64_t GetFileSize(const char *file_name)
Definition: file.cc:175
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:31
Status Initialize(const StreamInfo &stream_info, KeySource *encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds)
Definition: ts_segmenter.cc:34