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  BufferWriter buffer;
22  buffer.AppendInt(timestamp);
23  return std::string(buffer.Buffer(), buffer.Buffer() + buffer.Size());
24 }
25 } // namespace
26 
27 PackedAudioSegmenter::PackedAudioSegmenter() = default;
28 PackedAudioSegmenter::~PackedAudioSegmenter() = default;
29 
31  const StreamType stream_type = stream_info.stream_type();
32  if (stream_type != StreamType::kStreamAudio) {
33  LOG(ERROR) << "PackedAudioSegmenter cannot handle stream type "
34  << stream_type;
35  return Status(error::MUXER_FAILURE, "Unsupported stream type.");
36  }
37 
38  codec_ = stream_info.codec();
39  audio_codec_config_ = stream_info.codec_config();
40  timescale_scale_ = kPackedAudioTimescale / stream_info.time_scale();
41 
42  if (codec_ == kCodecAAC) {
43  adts_converter_ = CreateAdtsConverter();
44  if (!adts_converter_->Parse(audio_codec_config_)) {
45  return Status(error::MUXER_FAILURE, "Invalid audio codec configuration.");
46  }
47  }
48 
49  return Status::OK;
50 }
51 
53  if (sample.is_encrypted() && audio_setup_information_.empty())
54  RETURN_IF_ERROR(EncryptionAudioSetup(sample));
55 
56  if (start_of_new_segment_) {
57  StartNewSegment(sample);
58  start_of_new_segment_ = false;
59  }
60 
61  if (adts_converter_) {
62  std::vector<uint8_t> audio_frame(sample.data(),
63  sample.data() + sample.data_size());
64  if (!adts_converter_->ConvertToADTS(&audio_frame))
65  return Status(error::MUXER_FAILURE, "Failed to convert to ADTS.");
66  segment_buffer_.AppendArray(audio_frame.data(), audio_frame.size());
67  } else {
68  segment_buffer_.AppendArray(sample.data(), sample.data_size());
69  }
70  return Status::OK;
71 }
72 
74  start_of_new_segment_ = true;
75  return Status::OK;
76 }
77 
79  return timescale_scale_;
80 }
81 
82 std::unique_ptr<AACAudioSpecificConfig>
83 PackedAudioSegmenter::CreateAdtsConverter() {
84  return std::unique_ptr<AACAudioSpecificConfig>(new AACAudioSpecificConfig);
85 }
86 
87 std::unique_ptr<Id3Tag> PackedAudioSegmenter::CreateId3Tag() {
88  return std::unique_ptr<Id3Tag>(new Id3Tag);
89 }
90 
91 Status PackedAudioSegmenter::EncryptionAudioSetup(const MediaSample& sample) {
92  // For codecs other than AC3, audio setup data is the audio codec
93  // configuration data.
94  const uint8_t* audio_setup_data = audio_codec_config_.data();
95  size_t audio_setup_data_size = audio_codec_config_.size();
96  if (codec_ == kCodecAC3) {
97  // https://goo.gl/N7Tvqi MPEG-2 Stream Encryption Format for HTTP Live
98  // Streaming 2.3.2.2 AC-3 Setup: For AC-3, the setup_data in the
99  // audio_setup_information is the first 10 bytes of the audio data (the
100  // syncframe()).
101  const size_t kSetupDataSize = 10u;
102  if (sample.data_size() < kSetupDataSize) {
103  LOG(ERROR) << "Sample is too small for AC3: " << sample.data_size();
104  return Status(error::MUXER_FAILURE, "Sample is too small for AC3.");
105  }
106  audio_setup_data = sample.data();
107  audio_setup_data_size = kSetupDataSize;
108  }
109 
110  BufferWriter buffer;
111  if (!WriteAudioSetupInformation(codec_, audio_setup_data,
112  audio_setup_data_size, &buffer)) {
113  return Status(error::MUXER_FAILURE,
114  "Failed to write audio setup information.");
115  }
116  audio_setup_information_.assign(buffer.Buffer(),
117  buffer.Buffer() + buffer.Size());
118  return Status::OK;
119 }
120 
121 void PackedAudioSegmenter::StartNewSegment(const MediaSample& sample) {
122  segment_buffer_.Clear();
123 
124  // Use a unique_ptr so it can be mocked for testing.
125  std::unique_ptr<Id3Tag> id3_tag = CreateId3Tag();
126  id3_tag->AddPrivateFrame(kTimestampOwnerIdentifier,
127  TimestampToString(sample.pts() * timescale_scale_));
128  if (!audio_setup_information_.empty()) {
129  id3_tag->AddPrivateFrame(kAudioDescriptionOwnerIdentifier,
130  audio_setup_information_);
131  }
132  CHECK(id3_tag->WriteToBuffer(&segment_buffer_));
133 }
134 
135 } // namespace media
136 } // namespace shaka
Abstract class holds stream information.
Definition: stream_info.h:59
const uint8_t * Buffer() const
Definition: buffer_writer.h:61
All the methods that are virtual are virtual for mocking.
virtual Status Initialize(const StreamInfo &stream_info)
Class to hold a media sample.
Definition: media_sample.h:22
virtual Status AddSample(const MediaSample &sample)