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