Shaka Packager SDK
packed_audio_writer.cc
1 // Copyright 2018 Google LLC. 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/packed_audio/packed_audio_writer.h"
8 
9 #include "packager/media/base/muxer_util.h"
10 #include "packager/media/formats/packed_audio/packed_audio_segmenter.h"
11 #include "packager/status_macros.h"
12 
13 namespace shaka {
14 namespace media {
15 
17  : Muxer(muxer_options),
18  transport_stream_timestamp_offset_(
19  muxer_options.transport_stream_timestamp_offset_ms *
20  kPackedAudioTimescale / 1000),
21  segmenter_(new PackedAudioSegmenter(transport_stream_timestamp_offset_)) {
22 }
23 
24 PackedAudioWriter::~PackedAudioWriter() = default;
25 
26 Status PackedAudioWriter::InitializeMuxer() {
27  if (streams().size() > 1u)
28  return Status(error::MUXER_FAILURE, "Cannot handle more than one streams.");
29 
30  RETURN_IF_ERROR(segmenter_->Initialize(*streams()[0]));
31 
32  if (options().segment_template.empty()) {
33  const std::string& file_name = options().output_file_name;
34  DCHECK(!file_name.empty());
35  output_file_.reset(File::Open(file_name.c_str(), "w"));
36  if (!output_file_) {
37  return Status(error::FILE_FAILURE,
38  "Cannot open file for write " + file_name);
39  }
40  }
41 
42  if (muxer_listener()) {
43  muxer_listener()->OnMediaStart(options(), *streams().front(),
44  kPackedAudioTimescale,
45  MuxerListener::kContainerPackedAudio);
46  }
47  return Status::OK;
48 }
49 
50 Status PackedAudioWriter::Finalize() {
51  if (output_file_)
52  RETURN_IF_ERROR(CloseFile(std::move(output_file_)));
53 
54  if (muxer_listener()) {
55  muxer_listener()->OnMediaEnd(
56  media_ranges_, total_duration_ * segmenter_->TimescaleScale());
57  }
58  return Status::OK;
59 }
60 
61 Status PackedAudioWriter::AddMediaSample(size_t stream_id,
62  const MediaSample& sample) {
63  DCHECK_EQ(stream_id, 0u);
64  return segmenter_->AddSample(sample);
65 }
66 
67 Status PackedAudioWriter::FinalizeSegment(size_t stream_id,
68  const SegmentInfo& segment_info) {
69  DCHECK_EQ(stream_id, 0u);
70  // PackedAudio does not support subsegment.
71  if (segment_info.is_subsegment)
72  return Status::OK;
73 
74  RETURN_IF_ERROR(segmenter_->FinalizeSegment());
75 
76  const uint64_t segment_timestamp =
77  segment_info.start_timestamp * segmenter_->TimescaleScale();
78  std::string segment_path =
79  options().segment_template.empty()
80  ? options().output_file_name
81  : GetSegmentName(options().segment_template, segment_timestamp,
82  segment_number_++, options().bandwidth);
83 
84  // Save |segment_size| as it will be cleared after writing.
85  const size_t segment_size = segmenter_->segment_buffer()->Size();
86 
87  RETURN_IF_ERROR(WriteSegment(segment_path, segmenter_->segment_buffer()));
88  total_duration_ += segment_info.duration;
89 
90  if (muxer_listener()) {
91  muxer_listener()->OnNewSegment(
92  segment_path, segment_timestamp + transport_stream_timestamp_offset_,
93  segment_info.duration * segmenter_->TimescaleScale(), segment_size);
94  }
95  return Status::OK;
96 }
97 
98 Status PackedAudioWriter::WriteSegment(const std::string& segment_path,
99  BufferWriter* segment_buffer) {
100  std::unique_ptr<File, FileCloser> file;
101  if (output_file_) {
102  // This is in single segment mode.
103  Range range;
104  range.start = media_ranges_.subsegment_ranges.empty()
105  ? 0
106  : (media_ranges_.subsegment_ranges.back().end + 1);
107  range.end = range.start + segment_buffer->Size() - 1;
108  media_ranges_.subsegment_ranges.push_back(range);
109  } else {
110  file.reset(File::Open(segment_path.c_str(), "w"));
111  if (!file) {
112  return Status(error::FILE_FAILURE,
113  "Cannot open file for write " + segment_path);
114  }
115  }
116 
117  RETURN_IF_ERROR(segment_buffer->WriteToFile(output_file_ ? output_file_.get()
118  : file.get()));
119 
120  if (file)
121  RETURN_IF_ERROR(CloseFile(std::move(file)));
122  return Status::OK;
123 }
124 
125 Status PackedAudioWriter::CloseFile(std::unique_ptr<File, FileCloser> file) {
126  std::string file_name = file->file_name();
127  if (!file.release()->Close()) {
128  return Status(
129  error::FILE_FAILURE,
130  "Cannot close file " + file_name +
131  ", possibly file permission issue or running out of disk space.");
132  }
133  return Status::OK;
134 }
135 
136 } // namespace media
137 } // namespace shaka
shaka::media::Muxer
Definition: muxer.h:30
shaka::media::MuxerOptions::segment_template
std::string segment_template
Definition: muxer_options.h:40
shaka
All the methods that are virtual are virtual for mocking.
Definition: gflags_hex_bytes.cc:11
shaka::media::MuxerListener::MediaRanges::subsegment_ranges
std::vector< Range > subsegment_ranges
Definition: muxer_listener.h:54
shaka::Status
Definition: status.h:110
shaka::media::MuxerOptions
This structure contains the list of configuration options for Muxer.
Definition: muxer_options.h:20
shaka::media::PackedAudioSegmenter
Definition: packed_audio_segmenter.h:40
shaka::media::MuxerOptions::output_file_name
std::string output_file_name
Definition: muxer_options.h:34
shaka::media::MuxerListener::OnMediaEnd
virtual void OnMediaEnd(const MediaRanges &media_ranges, float duration_seconds)=0
shaka::media::PackedAudioWriter::PackedAudioWriter
PackedAudioWriter(const MuxerOptions &muxer_options)
Create a MP4Muxer object from MuxerOptions.
Definition: packed_audio_writer.cc:16
shaka::media::MuxerListener::OnMediaStart
virtual void OnMediaStart(const MuxerOptions &muxer_options, const StreamInfo &stream_info, uint32_t time_scale, ContainerType container_type)=0
shaka::media::MuxerListener::OnNewSegment
virtual void OnNewSegment(const std::string &segment_name, int64_t start_time, int64_t duration, uint64_t segment_file_size)=0
shaka::File::Open
virtual bool Open()=0
Internal open. Should not be used directly.