DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs 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/muxer_util.h"
12 #include "packager/media/base/status.h"
13 #include "packager/media/event/muxer_listener.h"
14 
15 namespace edash_packager {
16 namespace media {
17 namespace mp2t {
18 
19 namespace {
20 const double kTsTimescale = 90000;
21 } // namespace
22 
24  : muxer_options_(options),
25  listener_(listener),
26  ts_writer_(new TsWriter()),
27  pes_packet_generator_(new PesPacketGenerator()) {}
28 TsSegmenter::~TsSegmenter() {}
29 
31  if (muxer_options_.segment_template.empty())
32  return Status(error::MUXER_FAILURE, "Segment template not specified.");
33  if (!ts_writer_->Initialize(stream_info))
34  return Status(error::MUXER_FAILURE, "Failed to initialize TsWriter.");
35  if (!pes_packet_generator_->Initialize(stream_info)) {
36  return Status(error::MUXER_FAILURE,
37  "Failed to initialize PesPacketGenerator.");
38  }
39 
40  timescale_scale_ = kTsTimescale / stream_info.time_scale();
41  return Status::OK;
42 }
43 
45  return Flush();
46 }
47 
48 // First checks whether the sample is a key frame. If so and the segment has
49 // passed the segment duration, then flush the generator and write all the data
50 // to file.
51 Status TsSegmenter::AddSample(scoped_refptr<MediaSample> sample) {
52  const bool passed_segment_duration =
53  current_segment_total_sample_duration_ > muxer_options_.segment_duration;
54  if (sample->is_key_frame() && passed_segment_duration) {
55  Status status = Flush();
56  if (!status.ok())
57  return status;
58  }
59 
60  if (!ts_writer_file_opened_ && !sample->is_key_frame())
61  LOG(WARNING) << "A segment will start with a non key frame.";
62 
63  if (!pes_packet_generator_->PushSample(sample)) {
64  return Status(error::MUXER_FAILURE,
65  "Failed to add sample to PesPacketGenerator.");
66  }
67 
68  const double scaled_sample_duration = sample->duration() * timescale_scale_;
69  current_segment_total_sample_duration_ +=
70  scaled_sample_duration / kTsTimescale;
71 
72  return WritePesPacketsToFile();
73 }
74 
75 void TsSegmenter::InjectTsWriterForTesting(scoped_ptr<TsWriter> writer) {
76  ts_writer_ = writer.Pass();
77 }
78 
80  scoped_ptr<PesPacketGenerator> generator) {
81  pes_packet_generator_ = generator.Pass();
82 }
83 
85  ts_writer_file_opened_ = value;
86 }
87 
88 Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) {
89  if (ts_writer_file_opened_)
90  return Status::OK;
91  const std::string segment_name =
92  GetSegmentName(muxer_options_.segment_template, next_pts,
93  segment_number_++, muxer_options_.bandwidth);
94  if (!ts_writer_->NewSegment(segment_name))
95  return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");
96  current_segment_start_time_ = next_pts;
97  current_segment_path_ = segment_name;
98  ts_writer_file_opened_ = true;
99  return Status::OK;
100 }
101 
102 Status TsSegmenter::WritePesPacketsToFile() {
103  while (pes_packet_generator_->NumberOfReadyPesPackets() > 0u) {
104  scoped_ptr<PesPacket> pes_packet =
105  pes_packet_generator_->GetNextPesPacket();
106 
107  Status status = OpenNewSegmentIfClosed(pes_packet->pts());
108  if (!status.ok())
109  return status;
110 
111  if (!ts_writer_->AddPesPacket(pes_packet.Pass()))
112  return Status(error::MUXER_FAILURE, "Failed to add PES packet.");
113  }
114  return Status::OK;
115 }
116 
117 Status TsSegmenter::Flush() {
118  if (!pes_packet_generator_->Flush()) {
119  return Status(error::MUXER_FAILURE,
120  "Failed to flush PesPacketGenerator.");
121  }
122  Status status = WritePesPacketsToFile();
123  if (!status.ok())
124  return status;
125 
126  // This method may be called from Finalize() so ts_writer_file_opened_ could
127  // be false.
128  if (ts_writer_file_opened_) {
129  if (!ts_writer_->FinalizeSegment()) {
130  return Status(error::MUXER_FAILURE, "Failed to finalize TsWriter.");
131  }
132  if (listener_) {
133  const int64_t file_size =
134  File::GetFileSize(current_segment_path_.c_str());
135  listener_->OnNewSegment(
136  current_segment_path_, current_segment_start_time_,
137  current_segment_total_sample_duration_ * kTsTimescale, file_size);
138  }
139  ts_writer_file_opened_ = false;
140  }
141  current_segment_total_sample_duration_ = 0.0;
142  current_segment_start_time_ = 0;
143  current_segment_path_.clear();
144  return Status::OK;
145 }
146 
147 } // namespace mp2t
148 } // namespace media
149 } // namespace edash_packager
Status Initialize(const StreamInfo &stream_info)
Definition: ts_segmenter.cc:30
Abstract class holds stream information.
Definition: stream_info.h:26
void SetTsWriterFileOpenedForTesting(bool value)
Only for testing.
Definition: ts_segmenter.cc:84
TsSegmenter(const MuxerOptions &options, MuxerListener *listener)
Definition: ts_segmenter.cc:23
void InjectTsWriterForTesting(scoped_ptr< TsWriter > writer)
Only for testing.
Definition: ts_segmenter.cc:75
Status AddSample(scoped_refptr< MediaSample > sample)
Definition: ts_segmenter.cc:51
void InjectPesPacketGeneratorForTesting(scoped_ptr< PesPacketGenerator > generator)
Only for testing.
Definition: ts_segmenter.cc:79
virtual void OnNewSegment(const std::string &segment_name, uint64_t start_time, uint64_t duration, uint64_t segment_file_size)=0
This structure contains the list of configuration options for Muxer.
Definition: muxer_options.h:18
static int64_t GetFileSize(const char *file_name)
Definition: file.cc:175