Shaka Packager SDK
packed_audio_segmenter.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_segmenter.h>
8 
9 #include <memory>
10 
11 #include <absl/log/check.h>
12 
13 #include <packager/macros/status.h>
14 #include <packager/media/base/id3_tag.h>
15 #include <packager/media/base/media_sample.h>
16 #include <packager/media/codecs/aac_audio_specific_config.h>
17 #include <packager/media/codecs/hls_audio_util.h>
18 
19 namespace shaka {
20 namespace media {
21 namespace {
22 std::string TimestampToString(int64_t timestamp) {
23  // https://tools.ietf.org/html/rfc8216 The ID3 payload MUST be a 33-bit MPEG-2
24  // Program Elementary Stream timestamp expressed as a big-endian eight-octet
25  // number, with the upper 31 bits set to zero.
26  timestamp &= 0x1FFFFFFFFull;
27 
28  BufferWriter buffer;
29  buffer.AppendInt(timestamp);
30  return std::string(buffer.Buffer(), buffer.Buffer() + buffer.Size());
31 }
32 } // namespace
33 
35  int32_t transport_stream_timestamp_offset)
36  : transport_stream_timestamp_offset_(transport_stream_timestamp_offset) {}
37 
38 PackedAudioSegmenter::~PackedAudioSegmenter() = default;
39 
40 Status PackedAudioSegmenter::Initialize(const StreamInfo& stream_info) {
41  const StreamType stream_type = stream_info.stream_type();
42  if (stream_type != StreamType::kStreamAudio) {
43  LOG(ERROR) << "PackedAudioSegmenter cannot handle stream type "
44  << stream_type;
45  return Status(error::MUXER_FAILURE, "Unsupported stream type.");
46  }
47 
48  codec_ = stream_info.codec();
49  audio_codec_config_ = stream_info.codec_config();
50  timescale_scale_ = kPackedAudioTimescale / stream_info.time_scale();
51 
52  if (codec_ == kCodecAAC) {
53  adts_converter_ = CreateAdtsConverter();
54  if (!adts_converter_->Parse(audio_codec_config_)) {
55  return Status(error::MUXER_FAILURE, "Invalid audio codec configuration.");
56  }
57  }
58 
59  return Status::OK;
60 }
61 
63  if (sample.is_encrypted() && audio_setup_information_.empty())
64  RETURN_IF_ERROR(EncryptionAudioSetup(sample));
65 
66  if (start_of_new_segment_) {
67  RETURN_IF_ERROR(StartNewSegment(sample));
68  start_of_new_segment_ = false;
69  }
70 
71  if (adts_converter_) {
72  std::vector<uint8_t> audio_frame;
73  if (!adts_converter_->ConvertToADTS(sample.data(), sample.data_size(),
74  &audio_frame))
75  return Status(error::MUXER_FAILURE, "Failed to convert to ADTS.");
76  segment_buffer_.AppendArray(audio_frame.data(), audio_frame.size());
77  } else {
78  segment_buffer_.AppendArray(sample.data(), sample.data_size());
79  }
80  return Status::OK;
81 }
82 
84  start_of_new_segment_ = true;
85  return Status::OK;
86 }
87 
89  return timescale_scale_;
90 }
91 
92 std::unique_ptr<AACAudioSpecificConfig>
93 PackedAudioSegmenter::CreateAdtsConverter() {
94  return std::unique_ptr<AACAudioSpecificConfig>(new AACAudioSpecificConfig);
95 }
96 
97 std::unique_ptr<Id3Tag> PackedAudioSegmenter::CreateId3Tag() {
98  return std::unique_ptr<Id3Tag>(new Id3Tag);
99 }
100 
101 Status PackedAudioSegmenter::EncryptionAudioSetup(const MediaSample& sample) {
102  // For codecs other than AC3, audio setup data is the audio codec
103  // configuration data.
104  const uint8_t* audio_setup_data = audio_codec_config_.data();
105  size_t audio_setup_data_size = audio_codec_config_.size();
106  if (codec_ == kCodecAC3) {
107  // https://goo.gl/N7Tvqi MPEG-2 Stream Encryption Format for HTTP Live
108  // Streaming 2.3.2.2 AC-3 Setup: For AC-3, the setup_data in the
109  // audio_setup_information is the first 10 bytes of the audio data (the
110  // syncframe()).
111  const size_t kSetupDataSize = 10u;
112  if (sample.data_size() < kSetupDataSize) {
113  LOG(ERROR) << "Sample is too small for AC3: " << sample.data_size();
114  return Status(error::MUXER_FAILURE, "Sample is too small for AC3.");
115  }
116  audio_setup_data = sample.data();
117  audio_setup_data_size = kSetupDataSize;
118  }
119 
120  BufferWriter buffer;
121  if (!WriteAudioSetupInformation(codec_, audio_setup_data,
122  audio_setup_data_size, &buffer)) {
123  return Status(error::MUXER_FAILURE,
124  "Failed to write audio setup information.");
125  }
126  audio_setup_information_.assign(buffer.Buffer(),
127  buffer.Buffer() + buffer.Size());
128  return Status::OK;
129 }
130 
131 Status PackedAudioSegmenter::StartNewSegment(const MediaSample& sample) {
132  segment_buffer_.Clear();
133 
134  const int64_t pts =
135  sample.pts() * timescale_scale_ + transport_stream_timestamp_offset_;
136  if (pts < 0) {
137  LOG(ERROR) << "Seeing negative timestamp " << pts
138  << " after applying offset "
139  << transport_stream_timestamp_offset_
140  << ". Please check if it is expected. Adjust "
141  "--transport_stream_timestamp_offset_ms if needed.";
142  return Status(error::MUXER_FAILURE, "Unsupported negative timestamp.");
143  }
144 
145  // Use a unique_ptr so it can be mocked for testing.
146  std::unique_ptr<Id3Tag> id3_tag = CreateId3Tag();
147  id3_tag->AddPrivateFrame(kTimestampOwnerIdentifier, TimestampToString(pts));
148  if (!audio_setup_information_.empty()) {
149  id3_tag->AddPrivateFrame(kAudioDescriptionOwnerIdentifier,
150  audio_setup_information_);
151  }
152  CHECK(id3_tag->WriteToBuffer(&segment_buffer_));
153 
154  return Status::OK;
155 }
156 
157 } // namespace media
158 } // namespace shaka
Class to hold a media sample.
Definition: media_sample.h:25
virtual Status Initialize(const StreamInfo &stream_info)
virtual Status AddSample(const MediaSample &sample)
PackedAudioSegmenter(int32_t transport_stream_timestamp_offset)
Abstract class holds stream information.
Definition: stream_info.h:71
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66