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