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