-
Notifications
You must be signed in to change notification settings - Fork 19
/
baseline_profile.c
434 lines (370 loc) · 18.3 KB
/
baseline_profile.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
// -------------------------------------------------------------------------------------------
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// This file is part of the AWS CDI-SDK, licensed under the BSD 2-Clause "Simplified" License.
// License details at: https://github.com/aws/aws-cdi-sdk/blob/mainline/LICENSE
// -------------------------------------------------------------------------------------------
/**
* @file
* @brief
* This file contains the functions and other definitions that comprise the CDI AVM baseline profile.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cdi_baseline_profile_api.h"
#include "cdi_logger_api.h"
#include "utilities_api.h"
//*********************************************************************************************************************
//***************************************** START OF DEFINITIONS AND TYPES ********************************************
//*********************************************************************************************************************
/// @brief Used to determine if static instance data used within this file has been initialized.
static int initialized = false;
/// @brief Statically allocated mutex used to make initialization of profile data thread-safe.
static CdiStaticMutexType mutex_lock = CDI_STATIC_MUTEX_INITIALIZER;
/// @brief Value used to define the maximum number of profiles stored for each profile type.
#define PROFILES_MAX (10)
/// @brief Type used to hold baseline profile version and V-table to required APIs.
typedef struct {
CdiAvmBaselineProfileVersion version; ///< Profile version.
CdiAvmVTableApi vtable_api; ///< Profile V-table API.
} BaselineProfileData;
//*********************************************************************************************************************
//*********************************************** START OF VARIABLES **************************************************
//*********************************************************************************************************************
/// @brief Array of baseline profiles, with version and v-tables for each one. Indexed by profile type.
static BaselineProfileData baseline_profile_array[CDI_BASELINE_AVM_PAYLOAD_TYPE_ENUM_COUNT][PROFILES_MAX] = {0};
/// @brief Array of number of profiles for each profile type.
static int profile_type_count_array[CDI_BASELINE_AVM_PAYLOAD_TYPE_ENUM_COUNT] = {0};
/**
* Table for converting between the supported AVM media types and the URIs associated with them.
*/
static const CdiEnumStringKey avm_uri_strings[] = {
// kCdiAvmNotBaseline is intentionally missing.
{ kCdiAvmVideo, "https://cdi.elemental.com/specs/baseline-video" },
{ kCdiAvmAudio, "https://cdi.elemental.com/specs/baseline-audio" },
{ kCdiAvmAncillary, "https://cdi.elemental.com/specs/baseline-ancillary-data" },
{ CDI_INVALID_ENUM_VALUE, NULL } // End of the array
};
/// Enum/string keys for CdiAvmPayloadType.
static const CdiEnumStringKey payload_type_key_array[] = {
{ kCdiAvmNotBaseline, "not baseline" },
{ kCdiAvmVideo, "Video" },
{ kCdiAvmAudio, "Audio" },
{ kCdiAvmAncillary, "Ancillary" },
{ CDI_INVALID_ENUM_VALUE, NULL } // End of the array
};
//*********************************************************************************************************************
//******************************************* START OF STATIC FUNCTIONS ***********************************************
//*********************************************************************************************************************
/**
* @brief Convert an enum key type to a profile type (same thing as payload type).
*
* @param key_type Enum value of key type to convert.
*
* @return Enum value of profile type.
*/
static CdiBaselineAvmPayloadType EnumStringKeyTypeToPayloadType(CdiAvmBaselineEnumStringKeyTypes key_type)
{
CdiBaselineAvmPayloadType payload_type = kCdiAvmNotBaseline;
switch (key_type) {
case kKeyAvmPayloadType:
// Nothing special needed.
break;
case kKeyAvmVideoSamplingType:
case kKeyAvmVideoAlphaChannelType:
case kKeyAvmVideoBitDepthType:
case kKeyAvmVideoColorimetryType:
case kKeyAvmVideoTcsType:
case kKeyAvmVideoRangeType:
payload_type = kCdiAvmVideo;
break;
case kKeyAvmAudioChannelGroupingType:
case kKeyAvmAudioSampleRateType:
payload_type = kCdiAvmAudio;
break;
}
return payload_type;
}
/**
* Initialize the AVM layer of the CDI-SDK.
*
* @return A value from the CdiReturnStatus enumeration.
*/
static CdiReturnStatus InitializeBaselineProfiles(void)
{
CdiReturnStatus ret = kCdiStatusOk;
// Register profiles based on 01.00.
extern CdiReturnStatus RegisterAvmBaselineProfiles_1_00(void);
ret = RegisterAvmBaselineProfiles_1_00();
if (kCdiStatusOk == ret) {
// Register profiles based on 02.00.
extern CdiReturnStatus RegisterAvmBaselineProfiles_2_00(void);
ret = RegisterAvmBaselineProfiles_2_00();
}
if (kCdiStatusOk != ret) {
CDI_LOG_THREAD(kLogError, "Failed to initialize baseline profiles. Error[%s].", ret);
}
return ret;
}
/**
* @brief Find the baseline profile for the specified payload type and version.
*
* @param payload_type Enum value of payload type (same as profile type)
* @param version_ptr Pointer to version to find. If NULL, finds first profile in table.
* @param warn_not_found When true, generate a warning when profile data could not be found.
*
* @return Pointer to found profile. NULL if none found.
*/
static BaselineProfileData* FindProfileVersion(CdiBaselineAvmPayloadType payload_type,
const CdiAvmBaselineProfileVersion* version_ptr, bool warn_not_found)
{
BaselineProfileData* profile_data_ptr = NULL;
if (kCdiAvmNotBaseline == payload_type) {
return NULL;
}
// profile_type_count_array and baseline_profile_array are indexed with kCdiAvmVideo at element 0.
const int payload_type_idx = payload_type - kCdiAvmVideo;
// No need to use lock if we have already completed initialization.
if (!CdiOsAtomicLoad32(&initialized)) {
CdiOsStaticMutexLock(mutex_lock);
if (!CdiOsAtomicLoad32(&initialized)) {
InitializeBaselineProfiles();
CdiOsAtomicStore32(&initialized, true);
}
CdiOsStaticMutexUnlock(mutex_lock);
}
// If desired version is NULL or 0.0, then default to first profile (ie. 1.0).
if (NULL == version_ptr || (0 == version_ptr->major && 0 == version_ptr->minor)) {
if (profile_type_count_array[payload_type_idx]) {
profile_data_ptr = &baseline_profile_array[payload_type_idx][0];
}
} else {
// Try to find matching profile version.
for (int i = 0; i < profile_type_count_array[payload_type_idx]; i++) {
BaselineProfileData* tmp_ptr = &baseline_profile_array[payload_type_idx][i];
if (tmp_ptr->version.major == version_ptr->major &&
tmp_ptr->version.minor == version_ptr->minor) {
profile_data_ptr = tmp_ptr;
break;
}
}
}
if (NULL == profile_data_ptr && warn_not_found) {
CDI_LOG_THREAD(kLogWarning, "Unable to find baseline profile v[%02d.%02d] for payload type[%s].",
version_ptr->major, version_ptr->minor,
CdiAvmKeyEnumToString(kKeyAvmPayloadType, payload_type, NULL));
}
return profile_data_ptr;
}
//*********************************************************************************************************************
//******************************************* START OF PUBLIC FUNCTIONS ***********************************************
//*********************************************************************************************************************
////////////////////////////////////////////////////////////////////////////////
// Doxygen commenting for these functions is in cdi_baseline_profile_api.h.
////////////////////////////////////////////////////////////////////////////////
CdiReturnStatus CdiAvmRegisterBaselineProfile(CdiBaselineAvmPayloadType media_type,
const char* profile_version_str,
CdiAvmVTableApi* vtable_api_ptr)
{
CdiReturnStatus ret = kCdiStatusOk;
CdiAvmBaselineProfileVersion version;
if (!CdiAvmParseBaselineVersionString(profile_version_str, &version)) {
CDI_LOG_THREAD(kLogError, "Unable to parse version string[%s]. Expected format is: xx.xx", profile_version_str);
return kCdiStatusInvalidParameter;
}
if (kCdiAvmNotBaseline == media_type) {
ret = kCdiStatusInvalidParameter;
}
if (initialized && NULL != FindProfileVersion(media_type, &version, false)) {
ret = kCdiStatusDuplicateBaselineVersion;
}
if (kCdiStatusOk == ret) {
// profile_type_count_array and baseline_profile_array are indexed with kCdiAvmVideo at element 0.
const int profile_type_idx = media_type - kCdiAvmVideo;
int count = profile_type_count_array[profile_type_idx];
if (count < CDI_ARRAY_ELEMENT_COUNT(baseline_profile_array[profile_type_idx])) {
baseline_profile_array[profile_type_idx][count].version = version;
baseline_profile_array[profile_type_idx][count].vtable_api = *vtable_api_ptr;
profile_type_count_array[profile_type_idx]++;
} else {
ret = kCdiStatusArraySizeExceeded;
}
}
return ret;
}
CdiReturnStatus CdiAvmMakeBaselineConfiguration(const CdiAvmBaselineConfig* baseline_config_ptr,
CdiAvmConfig* config_ptr, int* payload_unit_size_ptr)
{
return CdiAvmMakeBaselineConfiguration2((CdiAvmBaselineConfigCommon*)baseline_config_ptr, config_ptr,
payload_unit_size_ptr);
}
CdiReturnStatus CdiAvmMakeBaselineConfiguration2(const CdiAvmBaselineConfigCommon* baseline_config_ptr,
CdiAvmConfig* config_ptr, int* payload_unit_size_ptr)
{
CdiReturnStatus ret = kCdiStatusFatal;
// Zero out the whole structure for a clean start.
memset(config_ptr, 0, sizeof(*config_ptr));
BaselineProfileData* profile_data_ptr = FindProfileVersion(baseline_config_ptr->payload_type,
&baseline_config_ptr->version, true);
if (profile_data_ptr) {
// Copy the URI.
strncpy(config_ptr->uri, CdiUtilityEnumValueToString(avm_uri_strings, baseline_config_ptr->payload_type),
sizeof(config_ptr->uri) - 1);
if ((profile_data_ptr->vtable_api.make_config_ptr)(baseline_config_ptr, config_ptr, payload_unit_size_ptr)) {
ret = kCdiStatusOk;
}
}
return ret;
}
CdiReturnStatus CdiAvmParseBaselineConfiguration(const CdiAvmConfig* config_ptr,
CdiAvmBaselineConfig* baseline_config_ptr)
{
return CdiAvmParseBaselineConfiguration2(config_ptr, (CdiAvmBaselineConfigCommon*)baseline_config_ptr);
}
CdiReturnStatus CdiAvmParseBaselineConfiguration2(const CdiAvmConfig* config_ptr,
CdiAvmBaselineConfigCommon* baseline_config_ptr)
{
assert(config_ptr); // It is not valid to call CdiAvmParseBaselineConfiguration with config_ptr == NULL.
CdiReturnStatus ret = kCdiStatusNonFatal;
// Ensure that uri and data_size meet specifications.
if ((sizeof(config_ptr->uri) - 1) <= strlen(config_ptr->uri)) {
CDI_LOG_THREAD(kLogError, "uri string length[%u] exceeds specification[%u]", strlen(config_ptr->uri),
sizeof(config_ptr->uri));
} else if ((int)sizeof(config_ptr->data) < config_ptr->data_size) {
CDI_LOG_THREAD(kLogError, "data_size value[%u] exceeds specification[%u]", config_ptr->data_size,
sizeof(config_ptr->data));
} else {
int key = CdiUtilityStringToEnumValue(avm_uri_strings, config_ptr->uri);
CdiAvmBaselineConfigCommon common_config = { 0 };
if (CDI_INVALID_ENUM_VALUE != key) {
// This is a valid baseline profile, set its type.
common_config.payload_type = (CdiBaselineAvmPayloadType)key;
// Parse version info, so we can tell which baseline profile parser to use.
static const char profile_str[] = "cdi_profile_version=";
const char* found_str = strstr((const char*)config_ptr->data, profile_str);
if (found_str) {
found_str += strlen(profile_str);
char str_buf[6];
found_str += CdiOsStrCpy(str_buf, sizeof(str_buf), found_str);
if (';' != *found_str) {
CDI_LOG_THREAD(kLogError, "Expected ';' at end of version 'xx.xx'. Found[%s].", found_str);
} else if (!CdiAvmParseBaselineVersionString(str_buf, &common_config.version)) {
CDI_LOG_THREAD(kLogError, "Unable to parse profile version 'xx.xx'. Found[%s].", str_buf);
} else {
// Successfully parsed the version, now try to find the corresponding profile.
BaselineProfileData* profile_data_ptr = FindProfileVersion(common_config.payload_type,
&common_config.version, false);
if (NULL == baseline_config_ptr) {
CDI_LOG_THREAD(kLogError, "No output address (baseline_config_ptr) provided.");
ret = kCdiStatusFatal;
} else if (profile_data_ptr) {
// Clear the entire structure then plug in the payload type and version number.
memset(baseline_config_ptr, 0, profile_data_ptr->vtable_api.structure_size);
baseline_config_ptr->payload_type = common_config.payload_type;
baseline_config_ptr->version = common_config.version;
// Have the version and payload type specific function fill in the rest.
if ((profile_data_ptr->vtable_api.parse_config_ptr)(config_ptr, baseline_config_ptr)) {
ret = kCdiStatusOk; // Successfully parsed, so change ret status to ok.
}
} else {
baseline_config_ptr->payload_type = kCdiAvmNotBaseline;
ret = kCdiStatusProfileNotSupported;
}
}
} else {
CDI_LOG_THREAD(kLogError, "Unable to parse profile version string 'cdi_profile_version='.");
}
}
}
return ret;
}
CdiReturnStatus CdiAvmGetBaselineUnitSize(const CdiAvmBaselineConfig* baseline_config_ptr,
int* payload_unit_size_ptr)
{
return CdiAvmGetBaselineUnitSize2((CdiAvmBaselineConfigCommon*)baseline_config_ptr, payload_unit_size_ptr);
}
CdiReturnStatus CdiAvmGetBaselineUnitSize2(const CdiAvmBaselineConfigCommon* baseline_config_ptr,
int* payload_unit_size_ptr)
{
CdiReturnStatus ret = kCdiStatusFatal;
BaselineProfileData* profile_data_ptr = FindProfileVersion(baseline_config_ptr->payload_type,
&baseline_config_ptr->version, true);
if (profile_data_ptr) {
ret = (profile_data_ptr->vtable_api.get_unit_size_ptr)(baseline_config_ptr, payload_unit_size_ptr);
}
return ret;
}
const char* CdiAvmKeyEnumToString(CdiAvmBaselineEnumStringKeyTypes key_type, int enum_value,
const CdiAvmBaselineProfileVersion* version_ptr)
{
const CdiEnumStringKey* array_ptr = CdiAvmKeyGetArray(key_type, version_ptr);
if (array_ptr) {
return CdiUtilityEnumValueToString(array_ptr, enum_value);
}
return NULL;
}
int CdiAvmKeyStringToEnum(CdiAvmBaselineEnumStringKeyTypes key_type, const char* name_str,
const CdiAvmBaselineProfileVersion* version_ptr)
{
const CdiEnumStringKey* array_ptr = CdiAvmKeyGetArray(key_type, version_ptr);
if (array_ptr) {
return CdiUtilityStringToEnumValue(array_ptr, name_str);
}
return CDI_INVALID_ENUM_VALUE;
}
const CdiEnumStringKey* CdiAvmKeyGetArray(CdiAvmBaselineEnumStringKeyTypes key_type,
const CdiAvmBaselineProfileVersion* version_ptr)
{
const CdiEnumStringKey* array_ptr = NULL;
if (kKeyAvmPayloadType == key_type) {
array_ptr = payload_type_key_array;
} else {
CdiBaselineAvmPayloadType payload_type = EnumStringKeyTypeToPayloadType(key_type);
BaselineProfileData* profile_data_ptr = FindProfileVersion(payload_type, version_ptr, true);
if (profile_data_ptr) {
array_ptr = (profile_data_ptr->vtable_api.key_get_array_ptr)(key_type);
}
}
return array_ptr;
}
bool CdiAvmParseBaselineVersionString(const char* version_str, CdiAvmBaselineProfileVersion* ret_version_ptr)
{
bool ret = false;
bool minor = false;
int accumulator = 0;
for (int i = 0; i <= 5; i++) {
const char c = version_str[i];
if ('\0' == c) {
if (minor) {
ret_version_ptr->minor = accumulator;
ret = true;
}
break;
} else if (!minor && ('.' == c)) {
ret_version_ptr->major = accumulator;
accumulator = 0;
minor = true;
} else if (('0' <= c) && ('9' >= c)) {
accumulator = accumulator * 10 + (c - '0');
} else {
break;
}
}
return ret;
}
CdiReturnStatus CdiAvmValidateBaselineVersionString(CdiBaselineAvmPayloadType media_type, const char* version_str,
CdiAvmBaselineProfileVersion* ret_version_ptr)
{
CdiAvmBaselineProfileVersion version;
if (NULL == version_str || !CdiAvmParseBaselineVersionString(version_str, &version)) {
return kCdiStatusInvalidParameter;
}
if (!FindProfileVersion(media_type, &version, false)) {
return kCdiStatusProfileNotSupported;
}
if (ret_version_ptr) {
*ret_version_ptr = version;
}
return kCdiStatusOk;
}