blob: 30d0ae6d6753e81ae7127aa6b9bce3a676e93e62 [file] [log] [blame]
Ray Essickdf27b042018-11-27 18:55:09 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
Harish Mahendrakar6c955302019-01-25 09:15:51 -080018#define LOG_TAG "OpusHeader"
Ray Essickdf27b042018-11-27 18:55:09 -080019#include <cstring>
Harish Mahendrakar6c955302019-01-25 09:15:51 -080020#include <inttypes.h>
Ray Essickdf27b042018-11-27 18:55:09 -080021#include <stdint.h>
22
23#include <log/log.h>
24
25#include "OpusHeader.h"
26
27namespace android {
28
29// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
30// mappings for up to 8 channels. This information is part of the Vorbis I
31// Specification:
32// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
33constexpr int kMaxChannels = 8;
34
35constexpr uint8_t kOpusChannelMap[kMaxChannels][kMaxChannels] = {
36 {0},
37 {0, 1},
38 {0, 2, 1},
39 {0, 1, 2, 3},
40 {0, 4, 1, 2, 3},
41 {0, 4, 1, 2, 3, 5},
42 {0, 4, 1, 2, 3, 5, 6},
43 {0, 6, 1, 2, 3, 4, 5, 7},
44};
45
Ray Essickdf27b042018-11-27 18:55:09 -080046// Size of the Opus header excluding optional mapping information.
47constexpr size_t kOpusHeaderSize = 19;
48// Offset to magic string that starts Opus header.
49constexpr size_t kOpusHeaderLabelOffset = 0;
50// Offset to Opus version in the Opus header.
51constexpr size_t kOpusHeaderVersionOffset = 8;
52// Offset to the channel count byte in the Opus header.
53constexpr size_t kOpusHeaderChannelsOffset = 9;
54// Offset to the pre-skip value in the Opus header.
55constexpr size_t kOpusHeaderSkipSamplesOffset = 10;
56// Offset to sample rate in the Opus header.
57constexpr size_t kOpusHeaderSampleRateOffset = 12;
58// Offset to the gain value in the Opus header.
59constexpr size_t kOpusHeaderGainOffset = 16;
60// Offset to the channel mapping byte in the Opus header.
61constexpr size_t kOpusHeaderChannelMappingOffset = 18;
62// Opus Header contains a stream map. The mapping values are in the header
63// beyond the always present |kOpusHeaderSize| bytes of data. The mapping
64// data contains stream count, coupling information, and per channel mapping
65// values:
66// - Byte 0: Number of streams.
67// - Byte 1: Number coupled.
68// - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping
69// values.
70// Offset to the number of streams in the Opus header.
71constexpr size_t kOpusHeaderNumStreamsOffset = 19;
72// Offset to the number of streams that are coupled in the Opus header.
73constexpr size_t kOpusHeaderNumCoupledStreamsOffset = 20;
74// Offset to the stream to channel mapping in the Opus header.
75constexpr size_t kOpusHeaderStreamMapOffset = 21;
Ray Essickdf27b042018-11-27 18:55:09 -080076
77// Default audio output channel layout. Used to initialize |stream_map| in
78// OpusHeader, and passed to opus_multistream_decoder_create() when the header
79// does not contain mapping information. The values are valid only for mono and
80// stereo output: Opus streams with more than 2 channels require a stream map.
81constexpr int kMaxChannelsWithDefaultLayout = 2;
Ray Essickdf27b042018-11-27 18:55:09 -080082
83static uint16_t ReadLE16(const uint8_t* data, size_t data_size, uint32_t read_offset) {
84 // check whether the 2nd byte is within the buffer
85 if (read_offset + 1 >= data_size) return 0;
86 uint16_t val;
87 val = data[read_offset];
88 val |= data[read_offset + 1] << 8;
89 return val;
90}
91
92// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header
93bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header) {
Harish Mahendrakar6c955302019-01-25 09:15:51 -080094 if (data == NULL) {
95 return false;
96 }
Ray Essickdf27b042018-11-27 18:55:09 -080097 if (data_size < kOpusHeaderSize) {
98 ALOGV("Header size is too small.");
99 return false;
100 }
101 header->channels = data[kOpusHeaderChannelsOffset];
102
103 if (header->channels < 1 || header->channels > kMaxChannels) {
104 ALOGV("Invalid Header, bad channel count: %d", header->channels);
105 return false;
106 }
107 header->skip_samples = ReadLE16(data, data_size, kOpusHeaderSkipSamplesOffset);
108 header->gain_db = static_cast<int16_t>(ReadLE16(data, data_size, kOpusHeaderGainOffset));
109 header->channel_mapping = data[kOpusHeaderChannelMappingOffset];
110 if (!header->channel_mapping) {
111 if (header->channels > kMaxChannelsWithDefaultLayout) {
112 ALOGV("Invalid Header, missing stream map.");
113 return false;
114 }
115 header->num_streams = 1;
116 header->num_coupled = header->channels > 1;
117 header->stream_map[0] = 0;
118 header->stream_map[1] = 1;
119 return true;
120 }
121 if (data_size < kOpusHeaderStreamMapOffset + header->channels) {
122 ALOGV("Invalid stream map; insufficient data for current channel "
123 "count: %d",
124 header->channels);
125 return false;
126 }
127 header->num_streams = data[kOpusHeaderNumStreamsOffset];
128 header->num_coupled = data[kOpusHeaderNumCoupledStreamsOffset];
Harish Mahendrakarfb844da2020-02-27 14:33:30 -0800129 if (header->num_coupled > header->num_streams ||
130 header->num_streams + header->num_coupled != header->channels) {
131 ALOGV("Inconsistent channel mapping, streams: %d coupled: %d channels: %d",
132 header->num_streams, header->num_coupled, header->channels);
Ray Essickdf27b042018-11-27 18:55:09 -0800133 return false;
134 }
Harish Mahendrakarfb844da2020-02-27 14:33:30 -0800135 for (int i = 0; i < header->channels; ++i) {
136 uint8_t value = data[kOpusHeaderStreamMapOffset + i];
137 if (value != 255 && value >= header->channels) {
138 ALOGV("Invalid channel mapping for index %i : %d", i, value);
139 return false;
140 }
141 header->stream_map[i] = value;
142 }
Ray Essickdf27b042018-11-27 18:55:09 -0800143 return true;
144}
145
146int WriteOpusHeader(const OpusHeader &header, int input_sample_rate,
147 uint8_t* output, size_t output_size) {
148 // See https://wiki.xiph.org/OggOpus#ID_Header.
Rakesh Kumar9a37b242021-03-30 20:26:26 +0530149 if (header.channels < 1 || header.channels > kMaxChannels) {
150 ALOGE("Invalid channel count: %d", header.channels);
151 return -1;
152 }
Ray Essickdf27b042018-11-27 18:55:09 -0800153 const size_t total_size = kOpusHeaderStreamMapOffset + header.channels;
154 if (output_size < total_size) {
155 ALOGE("Output buffer too small for header.");
156 return -1;
157 }
158
159 // ensure entire header is cleared, even though we overwrite much of it below
160 memset(output, 0, output_size);
161
162 // Set magic signature.
163 memcpy(output + kOpusHeaderLabelOffset, "OpusHead", 8);
164 // Set Opus version.
165 output[kOpusHeaderVersionOffset] = 1;
166 // Set channel count.
167 output[kOpusHeaderChannelsOffset] = (uint8_t)header.channels;
168 // Set pre-skip
169 memcpy(output + kOpusHeaderSkipSamplesOffset, &header.skip_samples, sizeof(uint16_t));
170 // Set original input sample rate in Hz.
171 memcpy(output + kOpusHeaderSampleRateOffset, &input_sample_rate, sizeof(uint32_t));
172 // Set output gain in dB.
173 memcpy(output + kOpusHeaderGainOffset, &header.gain_db, sizeof(uint16_t));
174
175 if (header.channels > 2) {
176 // Set channel mapping
177 output[kOpusHeaderChannelMappingOffset] = 1;
178 // Assuming no coupled streams. This should actually be
179 // channels() - |coupled_streams|.
180 output[kOpusHeaderNumStreamsOffset] = header.channels;
181 output[kOpusHeaderNumCoupledStreamsOffset] = 0;
182
183 // Set the actual stream map.
184 for (int i = 0; i < header.channels; ++i) {
185 output[kOpusHeaderStreamMapOffset + i] = kOpusChannelMap[header.channels - 1][i];
186 }
187 return kOpusHeaderStreamMapOffset + header.channels + 1;
188 } else {
189 output[kOpusHeaderChannelMappingOffset] = 0;
190 return kOpusHeaderChannelMappingOffset + 1;
191 }
192}
193
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800194int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate,
195 uint8_t* output, size_t outputSize, uint64_t codecDelay,
196 uint64_t seekPreRoll) {
197 if (outputSize < AOPUS_UNIFIED_CSD_MINSIZE) {
198 ALOGD("Buffer not large enough to hold unified OPUS CSD");
199 return -1;
200 }
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800201 int headerLen = 0;
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800202
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800203 // Add opus header
204 /*
205 Following is the CSD syntax for signalling OpusHeader
206 (http://wiki.xiph.org/OggOpus#ID_Header)
207
208 Marker (8 bytes) | Length (8 bytes) | OpusHeader
209
210 Markers supported:
211 AOPUS_CSD_OPUS_HEADER_MARKER - Signals Opus Header
212
213 Length should be a value within AOPUS_OPUSHEAD_MINSIZE and AOPUS_OPUSHEAD_MAXSIZE.
214 */
215
216 memcpy(output + headerLen, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE);
217 headerLen += AOPUS_MARKER_SIZE;
218
219 // Place holder for opusHeader Size
220 headerLen += AOPUS_LENGTH_SIZE;
221
222 int headerSize = WriteOpusHeader(header, inputSampleRate, output + headerLen,
Harish Mahendrakar7ec47842019-05-14 16:54:54 -0700223 outputSize - headerLen);
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800224 if (headerSize < 0) {
225 ALOGD("%s: WriteOpusHeader failed", __func__);
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800226 return -1;
227 }
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800228 headerLen += headerSize;
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800229
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800230 // Update opus headerSize after AOPUS_CSD_OPUS_HEADER_MARKER
231 uint64_t length = headerSize;
232 memcpy(output + AOPUS_MARKER_SIZE, &length, AOPUS_LENGTH_SIZE);
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800233
234 /*
235 Following is the CSD syntax for signalling codec delay and
236 seek pre-roll which is to be appended after OpusHeader
237
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800238 Marker (8 bytes) | Length (8 bytes) | Samples in ns (8 bytes)
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800239
240 Markers supported:
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800241 AOPUS_CSD_CODEC_DELAY_MARKER - codec delay as samples in ns, represented in 8 bytes
242 AOPUS_CSD_SEEK_PREROLL_MARKER - preroll adjustment as samples in ns, represented in 8 bytes
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800243
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800244 */
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800245 length = sizeof(codecDelay);
246 if (headerLen > (outputSize - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE - length)) {
247 ALOGD("Buffer not large enough to hold codec delay");
248 return -1;
249 }
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800250 // Add codec delay
251 memcpy(output + headerLen, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE);
252 headerLen += AOPUS_MARKER_SIZE;
253 memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE);
254 headerLen += AOPUS_LENGTH_SIZE;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800255 memcpy(output + headerLen, &codecDelay, length);
256 headerLen += length;
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800257
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800258 length = sizeof(seekPreRoll);
259 if (headerLen > (outputSize - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE - length)) {
260 ALOGD("Buffer not large enough to hold seek pre roll");
261 return -1;
262 }
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800263 // Add skip pre roll
264 memcpy(output + headerLen, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE);
265 headerLen += AOPUS_MARKER_SIZE;
266 memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE);
267 headerLen += AOPUS_LENGTH_SIZE;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800268 memcpy(output + headerLen, &seekPreRoll, length);
269 headerLen += length;
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800270
271 return headerLen;
272}
273
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800274bool IsOpusHeader(const uint8_t *data, size_t data_size) {
275 if (data_size < AOPUS_MARKER_SIZE) {
276 return false;
277 }
278
279 return !memcmp(data, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE);
280}
281
282bool GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800283 void **opusHeadBuf, size_t *opusHeadSize,
284 void **codecDelayBuf, size_t *codecDelaySize,
285 void **seekPreRollBuf, size_t *seekPreRollSize) {
286 *codecDelayBuf = NULL;
287 *codecDelaySize = 0;
288 *seekPreRollBuf = NULL;
289 *seekPreRollSize = 0;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800290 *opusHeadBuf = NULL;
291 *opusHeadSize = 0;
292
293 // AOPUS_MARKER_SIZE is 8 "OpusHead" is of size 8
294 if (data_size < 8)
295 return false;
296
297 // Check if the CSD is in legacy format
298 if (!memcmp("OpusHead", data, 8)) {
299 if (data_size < AOPUS_OPUSHEAD_MINSIZE || data_size > AOPUS_OPUSHEAD_MAXSIZE) {
300 ALOGD("Unexpected size for opusHeadSize %zu", data_size);
301 return false;
302 }
303 *opusHeadBuf = (void *)data;
304 *opusHeadSize = data_size;
305 return true;
306 } else if (memcmp(AOPUS_CSD_MARKER_PREFIX, data, AOPUS_CSD_MARKER_PREFIX_SIZE) == 0) {
Harish Mahendrakar3473d552019-10-21 14:43:26 -0700307 if (data_size < AOPUS_UNIFIED_CSD_MINSIZE || data_size > AOPUS_UNIFIED_CSD_MAXSIZE) {
308 ALOGD("Unexpected size for unified opus csd %zu", data_size);
309 return false;
310 }
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800311 size_t i = 0;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800312 bool found = false;
313 while (i <= data_size - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE) {
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800314 uint8_t *csdBuf = (uint8_t *)data + i;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800315 if (!memcmp(csdBuf, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE)) {
316 uint64_t value;
317 memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value));
318 if (value < AOPUS_OPUSHEAD_MINSIZE || value > AOPUS_OPUSHEAD_MAXSIZE) {
319 ALOGD("Unexpected size for opusHeadSize %" PRIu64, value);
320 return false;
321 }
322 i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value;
323 if (i > data_size) {
324 ALOGD("Marker signals a header that is larger than input");
325 return false;
326 }
327 *opusHeadBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
328 *opusHeadSize = value;
329 found = true;
330 } else if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) {
331 uint64_t value;
332 memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value));
333 if (value != sizeof(uint64_t)) {
334 ALOGD("Unexpected size for codecDelay %" PRIu64, value);
335 return false;
336 }
337 i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value;
338 if (i > data_size) {
339 ALOGD("Marker signals a header that is larger than input");
340 return false;
341 }
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800342 *codecDelayBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800343 *codecDelaySize = value;
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800344 } else if (!memcmp(csdBuf, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE)) {
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800345 uint64_t value;
346 memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value));
347 if (value != sizeof(uint64_t)) {
348 ALOGD("Unexpected size for seekPreRollSize %" PRIu64, value);
349 return false;
350 }
351 i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value;
352 if (i > data_size) {
353 ALOGD("Marker signals a header that is larger than input");
354 return false;
355 }
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800356 *seekPreRollBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800357 *seekPreRollSize = value;
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800358 } else {
359 i++;
360 }
361 }
Harish Mahendrakar6c955302019-01-25 09:15:51 -0800362 return found;
363 } else {
364 return false; // it isn't in either format
Harish Mahendrakarad69acb2019-01-15 11:58:29 -0800365 }
366}
367
Ray Essickdf27b042018-11-27 18:55:09 -0800368} // namespace android