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  uint32_t max_hd_pixels,
38  uint32_t max_uhd1_pixels,
39  double clear_lead_in_seconds) {
40  if (muxer_options_.segment_template.empty())
41  return Status(error::MUXER_FAILURE, "Segment template not specified.");
42  if (!ts_writer_->Initialize(stream_info))
43  return Status(error::MUXER_FAILURE, "Failed to initialize TsWriter.");
44  if (!pes_packet_generator_->Initialize(stream_info)) {
45  return Status(error::MUXER_FAILURE,
46  "Failed to initialize PesPacketGenerator.");
47  }
48 
49  if (encryption_key_source) {
50  std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey());
51  const KeySource::TrackType type =
52  GetTrackTypeForEncryption(stream_info, max_sd_pixels,
53  max_hd_pixels, max_uhd1_pixels);
54  Status status = encryption_key_source->GetKey(type, encryption_key.get());
55 
56  if (encryption_key->iv.empty()) {
57  if (!AesCryptor::GenerateRandomIv(FOURCC_cbcs, &encryption_key->iv)) {
58  return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
59  }
60  }
61  if (!status.ok())
62  return status;
63 
64  encryption_key_ = std::move(encryption_key);
65  clear_lead_in_seconds_ = clear_lead_in_seconds;
66 
67  if (listener_) {
68  // For now this only happens once, so send true.
69  const bool kIsInitialEncryptionInfo = true;
70  listener_->OnEncryptionInfoReady(
71  kIsInitialEncryptionInfo, FOURCC_cbcs, encryption_key_->key_id,
72  encryption_key_->iv, encryption_key_->key_system_info);
73  }
74 
75  status = NotifyEncrypted();
76  if (!status.ok())
77  return status;
78  }
79 
80  timescale_scale_ = kTsTimescale / stream_info.time_scale();
81  return Status::OK;
82 }
83 
85  return Status::OK;
86 }
87 
88 Status TsSegmenter::AddSample(std::shared_ptr<MediaSample> sample) {
89  if (!ts_writer_file_opened_ && !sample->is_key_frame())
90  LOG(WARNING) << "A segment will start with a non key frame.";
91 
92  if (!pes_packet_generator_->PushSample(sample)) {
93  return Status(error::MUXER_FAILURE,
94  "Failed to add sample to PesPacketGenerator.");
95  }
96  return WritePesPacketsToFile();
97 }
98 
99 void TsSegmenter::InjectTsWriterForTesting(std::unique_ptr<TsWriter> writer) {
100  ts_writer_ = std::move(writer);
101 }
102 
104  std::unique_ptr<PesPacketGenerator> generator) {
105  pes_packet_generator_ = std::move(generator);
106 }
107 
109  ts_writer_file_opened_ = value;
110 }
111 
112 Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) {
113  if (ts_writer_file_opened_)
114  return Status::OK;
115  const std::string segment_name =
116  GetSegmentName(muxer_options_.segment_template, next_pts,
117  segment_number_++, muxer_options_.bandwidth);
118  if (!ts_writer_->NewSegment(segment_name))
119  return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");
120  current_segment_path_ = segment_name;
121  ts_writer_file_opened_ = true;
122  return Status::OK;
123 }
124 
125 Status TsSegmenter::WritePesPacketsToFile() {
126  while (pes_packet_generator_->NumberOfReadyPesPackets() > 0u) {
127  std::unique_ptr<PesPacket> pes_packet =
128  pes_packet_generator_->GetNextPesPacket();
129 
130  Status status = OpenNewSegmentIfClosed(pes_packet->pts());
131  if (!status.ok())
132  return status;
133 
134  if (!ts_writer_->AddPesPacket(std::move(pes_packet)))
135  return Status(error::MUXER_FAILURE, "Failed to add PES packet.");
136  }
137  return Status::OK;
138 }
139 
140 Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp,
141  uint64_t duration) {
142  if (!pes_packet_generator_->Flush()) {
143  return Status(error::MUXER_FAILURE,
144  "Failed to flush PesPacketGenerator.");
145  }
146  Status status = WritePesPacketsToFile();
147  if (!status.ok())
148  return status;
149 
150  // This method may be called from Finalize() so ts_writer_file_opened_ could
151  // be false.
152  if (ts_writer_file_opened_) {
153  if (!ts_writer_->FinalizeSegment()) {
154  return Status(error::MUXER_FAILURE, "Failed to finalize TsWriter.");
155  }
156  if (listener_) {
157  const int64_t file_size =
158  File::GetFileSize(current_segment_path_.c_str());
159  listener_->OnNewSegment(current_segment_path_,
160  start_timestamp * timescale_scale_,
161  duration * timescale_scale_, file_size);
162  }
163  ts_writer_file_opened_ = false;
164  total_duration_in_seconds_ += duration * timescale_scale_ / kTsTimescale;
165  }
166  current_segment_path_.clear();
167  return NotifyEncrypted();
168 }
169 
170 Status TsSegmenter::NotifyEncrypted() {
171  if (encryption_key_ && total_duration_in_seconds_ >= clear_lead_in_seconds_) {
172  if (listener_)
173  listener_->OnEncryptionStart();
174 
175  if (!pes_packet_generator_->SetEncryptionKey(std::move(encryption_key_)))
176  return Status(error::INTERNAL_ERROR, "Failed to set encryption key.");
177  ts_writer_->SignalEncrypted();
178  }
179  return Status::OK;
180 }
181 
182 } // namespace mp2t
183 } // namespace media
184 } // 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:60
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
Status AddSample(std::shared_ptr< MediaSample > sample)
Definition: ts_segmenter.cc:88
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:107
void InjectPesPacketGeneratorForTesting(std::unique_ptr< PesPacketGenerator > generator)
Only for testing.
void SetTsWriterFileOpenedForTesting(bool value)
Only for testing.
void InjectTsWriterForTesting(std::unique_ptr< TsWriter > writer)
Only for testing.
Definition: ts_segmenter.cc:99
Status Initialize(const StreamInfo &stream_info, KeySource *encryption_key_source, uint32_t max_sd_pixels, uint32_t max_hd_pixels, uint32_t max_uhd1_pixels, double clear_lead_in_seconds)
Definition: ts_segmenter.cc:34
static int64_t GetFileSize(const char *file_name)
Definition: file.cc:176
Status FinalizeSegment(uint64_t start_timestamp, uint64_t duration)
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:30