initial commit
This commit is contained in:
commit
61bf09d8c4
84
README.md
Normal file
84
README.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Amazon Rekognition People Label Detection via KVS
|
||||||
|
This is the README for the whole people detection pipeline designed for real-time video analysis.
|
||||||
|
|
||||||
|
There are three Lambda functions placed inside this repository:
|
||||||
|
### 1. Kinesis Video Stream to Rekognition (`cctv-people-rekogniton`)
|
||||||
|
This function utilizes Amazon Rekognition's stream processor, which is configured to monitor an incoming Amazon Kinesis Video Stream for people label detection.
|
||||||
|
|
||||||
|
Note that this Lambda function is intended to process **a single Kinesis video stream only**. For a multi-camera setup, multiple deployments of this Lambda function is necessary.
|
||||||
|
|
||||||
|
This is built on top of AWS KVS Consumer Library for Python. The original repository can be found here: https://github.com/aws-samples/amazon-kinesis-video-streams-consumer-library-for-python/
|
||||||
|
|
||||||
|
### 2. SNS to DynamoDB and OpenSearch (`cctv-people-sns-dynamodb`)
|
||||||
|
This function writes a new entry to DynamoDB and Opensearch tables whenever there is a detection coming from Rekognition via SNS.
|
||||||
|
|
||||||
|
### 3. DynamoDB to Supabase (`cctv-people-dynamodb-supabase`)
|
||||||
|
This function acts an extension of the previous function, writing a new entry to Supabase whenever there is a new entry in DynamoDB.
|
||||||
|
|
||||||
|
## Prerequites
|
||||||
|
The following must already be set up before deploying this pipeline:
|
||||||
|
1. A working Kinesis video stream as input
|
||||||
|
2. An S3 Bucket where all frames with detection go
|
||||||
|
3. A SNS topic that will receive the notification coming from Rekognition
|
||||||
|
4. A DynamoDB table
|
||||||
|
5. An OpenSearch Service collection
|
||||||
|
|
||||||
|
It is advised to have all above components in the same region for easy configuration.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
### Part I. Kinesis Video Stream to Rekognition (`\cctv-people-rekogniton`)
|
||||||
|
|
||||||
|
1. Create an IAM role for the Rekognition stream processor. This role must contain the following AWS-managed policies:
|
||||||
|
- AmazonRekognitionServiceRole
|
||||||
|
- AmazonS3FullAccess
|
||||||
|
|
||||||
|
2. Provide the following details in `lambda_function.py`:
|
||||||
|
- Kinesis video stream name and ARN
|
||||||
|
- Stream processor name (must be unique to the KVS stream)
|
||||||
|
- S3 bucket name
|
||||||
|
- IAM role ARN
|
||||||
|
- SNS topic ARN
|
||||||
|
|
||||||
|
3. Package the folder as a ZIP file.
|
||||||
|
4. Create an AWS Lambda function and upload the ZIP file.
|
||||||
|
5. Add an EventBridge trigger with the schedule expression of `rate(5 minutes)`
|
||||||
|
|
||||||
|
### Part II. SNS to DynamoDB and OpenSearch (`\cctv-people-sns-dynamodb`)
|
||||||
|
|
||||||
|
1. Create a Lambda function and upload the `lambda_function.py` file.
|
||||||
|
2. Add a trigger to receive notifications from the SNS topic.
|
||||||
|
3. Add the following AWS-managed permissions to this Lambda function:
|
||||||
|
- AmazonDynamoDBFullAccess_v2
|
||||||
|
- AmazonOpenSearchIngestionFullAccess
|
||||||
|
- AmazonOpenSearchServiceFullAccess
|
||||||
|
- AmazonS3FullAccess
|
||||||
|
4. Add an inline policy for OpenSearch Serverless:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "Statement1",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "aoss:*",
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
5. [insert Grafana instructions here]
|
||||||
|
|
||||||
|
### 3. DynamoDB to Supabase (`\cctv-people-dynamodb-supabase`)
|
||||||
|
1. On a local machine, prepare a folder for Supabase installation.
|
||||||
|
2. Open a terminal pointed at this directory and run the following command:
|
||||||
|
```
|
||||||
|
pip3 install supabase --platform manylinux2014_x86_64 --python-version 3.12 --only-binary=:all: -t .
|
||||||
|
```
|
||||||
|
3. Place the `lambda_function.py` inside this folder.
|
||||||
|
4. Package the folder as a ZIP file.
|
||||||
|
5. Create an AWS Lambda function and upload the ZIP file.
|
||||||
|
6. Add the following AWS-managed permission to the IAM role for this Lambda:
|
||||||
|
- AmazonDynamoDBFullAccess_v2
|
||||||
|
7. Enable stream on the DynamoDB table. Set view type to **New image**.
|
||||||
|
8. Add trigger to the Lambda function to connect to the DynamoDB stream.
|
||||||
|
|
||||||
26
lambda/cctv-people-dynamodb-supabase/lambda_function.py
Normal file
26
lambda/cctv-people-dynamodb-supabase/lambda_function.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
from supabase import create_client, Client
|
||||||
|
|
||||||
|
url: str = "https://ruthqdjqcvazlregemdb.supabase.co"
|
||||||
|
key: str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InJ1dGhxZGpxY3ZhemxyZWdlbWRiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTk0MTA2NTEsImV4cCI6MjA3NDk4NjY1MX0.peoFylhJQeMbPDze_6VTfbJUGqvaUCFsIH-9A0Aa3EM"
|
||||||
|
supabase: Client = create_client(url, key)
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
if event['Records'][0]['eventName'] == 'INSERT':
|
||||||
|
new_item = event['Records'][0]['dynamodb']['NewImage']
|
||||||
|
response = (
|
||||||
|
supabase.table("cctv-people")
|
||||||
|
.insert({
|
||||||
|
'camera_id': new_item['camera_id']['S'],
|
||||||
|
'label_id': new_item['label_id']['S'],
|
||||||
|
'detect_timestamp': new_item['detect_timestamp']['S'],
|
||||||
|
'frame_timestamp': new_item['frame_timestamp']['S'],
|
||||||
|
'frame_url': new_item['frame_url']['S'],
|
||||||
|
'label_name': new_item['label_name']['S'],
|
||||||
|
'confidence': new_item['confidence']['N'],
|
||||||
|
'rekognition_response': json.loads(new_item['rekognition_response']['S'])
|
||||||
|
})
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
print(response)
|
||||||
4
lambda/cctv-people-rekognition/ebmlite/__init__.py
Normal file
4
lambda/cctv-people-rekognition/ebmlite/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from .core import *
|
||||||
|
from .core import SCHEMA_PATH, SCHEMATA, __all__
|
||||||
|
|
||||||
|
name = "ebmlite"
|
||||||
1598
lambda/cctv-people-rekognition/ebmlite/core.py
Normal file
1598
lambda/cctv-people-rekognition/ebmlite/core.py
Normal file
File diff suppressed because it is too large
Load Diff
233
lambda/cctv-people-rekognition/ebmlite/decoding.py
Normal file
233
lambda/cctv-people-rekognition/ebmlite/decoding.py
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
"""
|
||||||
|
Functions for decoding EBML elements and their values.
|
||||||
|
|
||||||
|
Note: this module does not decode `Document`, `BinaryElement`, or
|
||||||
|
`MasterElement` objects; these are handled entirely in `core.py`. `Document`
|
||||||
|
and `MasterElement` objects are special cases, and `BinaryElement` objects do
|
||||||
|
not require special decoding.
|
||||||
|
"""
|
||||||
|
__author__ = "David Randall Stokes, Connor Flanigan"
|
||||||
|
__copyright__ = "Copyright 2021, Mide Technology Corporation"
|
||||||
|
__credits__ = "David Randall Stokes, Connor Flanigan, Becker Awqatty, Derek Witt"
|
||||||
|
|
||||||
|
__all__ = ['readElementID', 'readElementSize', 'readFloat', 'readInt',
|
||||||
|
'readUInt', 'readDate', 'readString', 'readUnicode']
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import struct
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Pre-built structs for packing/unpacking various data types
|
||||||
|
_struct_uint32 = struct.Struct(">I")
|
||||||
|
_struct_uint64 = struct.Struct(">Q")
|
||||||
|
_struct_int64 = struct.Struct(">q")
|
||||||
|
_struct_float32 = struct.Struct(">f")
|
||||||
|
_struct_float64 = struct.Struct(">d")
|
||||||
|
|
||||||
|
# Direct references to struct methods. Makes things a marginally faster.
|
||||||
|
_struct_uint32_unpack = _struct_uint32.unpack
|
||||||
|
_struct_uint64_unpack = _struct_uint64.unpack
|
||||||
|
_struct_int64_unpack = _struct_int64.unpack
|
||||||
|
_struct_uint64_unpack_from = _struct_uint64.unpack_from
|
||||||
|
_struct_int64_unpack_from = _struct_int64.unpack_from
|
||||||
|
_struct_float32_unpack = _struct_float32.unpack
|
||||||
|
_struct_float64_unpack = _struct_float64.unpack
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# --- Reading and Decoding
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
def decodeIntLength(byte):
|
||||||
|
""" Extract the encoded size from an initial byte.
|
||||||
|
|
||||||
|
@return: The size, and the byte with the size removed (it is the first
|
||||||
|
byte of the value).
|
||||||
|
"""
|
||||||
|
# An inelegant implementation, but it's fast.
|
||||||
|
if byte >= 128:
|
||||||
|
return 1, byte & 0b1111111
|
||||||
|
elif byte >= 64:
|
||||||
|
return 2, byte & 0b111111
|
||||||
|
elif byte >= 32:
|
||||||
|
return 3, byte & 0b11111
|
||||||
|
elif byte >= 16:
|
||||||
|
return 4, byte & 0b1111
|
||||||
|
elif byte >= 8:
|
||||||
|
return 5, byte & 0b111
|
||||||
|
elif byte >= 4:
|
||||||
|
return 6, byte & 0b11
|
||||||
|
elif byte >= 2:
|
||||||
|
return 7, byte & 0b1
|
||||||
|
|
||||||
|
return 8, 0
|
||||||
|
|
||||||
|
|
||||||
|
def decodeIDLength(byte):
|
||||||
|
""" Extract the encoded ID size from an initial byte.
|
||||||
|
|
||||||
|
@return: The size and the original byte (it is part of the ID).
|
||||||
|
@raise IOError: raise if the length of an ID is invalid.
|
||||||
|
"""
|
||||||
|
if byte >= 128:
|
||||||
|
return 1, byte
|
||||||
|
elif byte >= 64:
|
||||||
|
return 2, byte
|
||||||
|
elif byte >= 32:
|
||||||
|
return 3, byte
|
||||||
|
elif byte >= 16:
|
||||||
|
return 4, byte
|
||||||
|
|
||||||
|
length, _ = decodeIntLength(byte)
|
||||||
|
raise IOError('Invalid length for ID: %d' % length)
|
||||||
|
|
||||||
|
|
||||||
|
def readElementID(stream):
|
||||||
|
""" Read an element ID from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@return: The decoded element ID and its length in bytes.
|
||||||
|
@raise IOError: raised if the length of the ID of an element is greater than 4 bytes.
|
||||||
|
"""
|
||||||
|
ch = stream.read(1)
|
||||||
|
length, eid = decodeIDLength(ord(ch))
|
||||||
|
|
||||||
|
if length > 4:
|
||||||
|
raise IOError('Cannot decode element ID with length > 4.')
|
||||||
|
if length > 1:
|
||||||
|
eid = _struct_uint32_unpack((ch + stream.read(length-1)
|
||||||
|
).rjust(4, b'\x00'))[0]
|
||||||
|
return eid, length
|
||||||
|
|
||||||
|
|
||||||
|
def readElementSize(stream):
|
||||||
|
""" Read an element size from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@return: The decoded size (or `None`) and the length of the
|
||||||
|
descriptor in bytes.
|
||||||
|
"""
|
||||||
|
ch = stream.read(1)
|
||||||
|
length, size = decodeIntLength(ord(ch))
|
||||||
|
|
||||||
|
if length > 1:
|
||||||
|
size = _struct_uint64_unpack((chr(size).encode('latin-1') +
|
||||||
|
stream.read(length - 1)
|
||||||
|
).rjust(8, b'\x00'))[0]
|
||||||
|
|
||||||
|
if size == (2**(7*length)) - 1:
|
||||||
|
# EBML 'unknown' size, all bytes 0xFF
|
||||||
|
size = None
|
||||||
|
|
||||||
|
return size, length
|
||||||
|
|
||||||
|
|
||||||
|
def readUInt(stream, size):
|
||||||
|
""" Read an unsigned integer from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@param size: The number of bytes to read from the stream.
|
||||||
|
@return: The decoded value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if size == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
data = stream.read(size)
|
||||||
|
return _struct_uint64_unpack_from(data.rjust(8, b'\x00'))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def readInt(stream, size):
|
||||||
|
""" Read a signed integer from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@param size: The number of bytes to read from the stream.
|
||||||
|
@return: The decoded value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if size == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
data = stream.read(size)
|
||||||
|
if data[0] & 0b10000000:
|
||||||
|
pad = b'\xff'
|
||||||
|
else:
|
||||||
|
pad = b'\x00'
|
||||||
|
return _struct_int64_unpack_from(data.rjust(8, pad))[0]
|
||||||
|
|
||||||
|
|
||||||
|
def readFloat(stream, size):
|
||||||
|
""" Read an floating point value from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@param size: The number of bytes to read from the stream.
|
||||||
|
@return: The decoded value.
|
||||||
|
@raise IOError: raised if the length of this floating point number is not
|
||||||
|
valid (0, 4, 8 bytes)
|
||||||
|
"""
|
||||||
|
if size == 4:
|
||||||
|
return _struct_float32_unpack(stream.read(size))[0]
|
||||||
|
elif size == 8:
|
||||||
|
return _struct_float64_unpack(stream.read(size))[0]
|
||||||
|
elif size == 0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
raise IOError("Cannot read floating point value of length %s; "
|
||||||
|
"only lengths of 0, 4, or 8 bytes supported." % size)
|
||||||
|
|
||||||
|
|
||||||
|
def readString(stream, size):
|
||||||
|
""" Read an ASCII string from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@param size: The number of bytes to read from the stream.
|
||||||
|
@return: The decoded value.
|
||||||
|
"""
|
||||||
|
if size == 0:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
value = stream.read(size)
|
||||||
|
value = value.partition(b'\x00')[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
return str(value, 'ascii')
|
||||||
|
except UnicodeDecodeError as ex:
|
||||||
|
warnings.warn(str(ex), UnicodeWarning)
|
||||||
|
return str(value, 'ascii', 'replace')
|
||||||
|
|
||||||
|
|
||||||
|
def readUnicode(stream, size):
|
||||||
|
""" Read an UTF-8 encoded string from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@param size: The number of bytes to read from the stream.
|
||||||
|
@return: The decoded value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if size == 0:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
data = stream.read(size)
|
||||||
|
data = data.partition(b'\x00')[0]
|
||||||
|
return str(data, 'utf_8')
|
||||||
|
|
||||||
|
|
||||||
|
def readDate(stream, size=8):
|
||||||
|
""" Read an EBML encoded date (nanoseconds since UTC 2001-01-01T00:00:00)
|
||||||
|
from a file (or file-like stream).
|
||||||
|
|
||||||
|
@param stream: The source file-like object.
|
||||||
|
@param size: The number of bytes to read from the stream.
|
||||||
|
@return: The decoded value (as `datetime.datetime`).
|
||||||
|
@raise IOError: raised if the length of the date is not 8 bytes.
|
||||||
|
"""
|
||||||
|
if size != 8:
|
||||||
|
raise IOError("Cannot read date value of length %d, only 8." % size)
|
||||||
|
data = stream.read(size)
|
||||||
|
nanoseconds = _struct_int64_unpack(data)[0]
|
||||||
|
delta = timedelta(microseconds=(nanoseconds // 1000))
|
||||||
|
return datetime(2001, 1, 1, tzinfo=None) + delta
|
||||||
289
lambda/cctv-people-rekognition/ebmlite/encoding.py
Normal file
289
lambda/cctv-people-rekognition/ebmlite/encoding.py
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
"""
|
||||||
|
Functions for encoding EBML elements and their values.
|
||||||
|
|
||||||
|
Note: this module does not encode Document or MasterElement objects; they are
|
||||||
|
special cases, handled in `core.py`.
|
||||||
|
"""
|
||||||
|
__author__ = "David Randall Stokes, Connor Flanigan"
|
||||||
|
__copyright__ = "Copyright 2021, Mide Technology Corporation"
|
||||||
|
__credits__ = "David Randall Stokes, Connor Flanigan, Becker Awqatty, Derek Witt"
|
||||||
|
|
||||||
|
__all__ = ['encodeBinary', 'encodeDate', 'encodeFloat', 'encodeId', 'encodeInt',
|
||||||
|
'encodeSize', 'encodeString', 'encodeUInt', 'encodeUnicode']
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from .decoding import _struct_uint64, _struct_int64
|
||||||
|
from .decoding import _struct_float32, _struct_float64
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# If no length is given, use the platform's size of a float.
|
||||||
|
DEFAULT_FLOAT_SIZE = 4 if sys.maxsize <= 2147483647 else 8
|
||||||
|
|
||||||
|
LENGTH_PREFIXES = [0,
|
||||||
|
0x80,
|
||||||
|
0x4000,
|
||||||
|
0x200000,
|
||||||
|
0x10000000,
|
||||||
|
0x0800000000,
|
||||||
|
0x040000000000,
|
||||||
|
0x02000000000000,
|
||||||
|
0x0100000000000000
|
||||||
|
]
|
||||||
|
|
||||||
|
# Translation table for removing invalid EBML string characters (32 < x < 127)
|
||||||
|
STRING_CHARACTERS = (b"?"*32 + bytearray(range(32, 127))).ljust(256, b'?')
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def getLength(val):
|
||||||
|
""" Calculate the encoded length of a value.
|
||||||
|
@param val: A value to be encoded, generally either an ID or a size for
|
||||||
|
an EBML element
|
||||||
|
@return The minimum length, in bytes, that can be used to represent val
|
||||||
|
"""
|
||||||
|
# Brute force it. Ugly but faster than calculating it.
|
||||||
|
if val <= 126:
|
||||||
|
return 1
|
||||||
|
elif val <= 16382:
|
||||||
|
return 2
|
||||||
|
elif val <= 2097150:
|
||||||
|
return 3
|
||||||
|
elif val <= 268435454:
|
||||||
|
return 4
|
||||||
|
elif val <= 34359738366:
|
||||||
|
return 5
|
||||||
|
elif val <= 4398046511102:
|
||||||
|
return 6
|
||||||
|
elif val <= 562949953421310:
|
||||||
|
return 7
|
||||||
|
else:
|
||||||
|
return 8
|
||||||
|
|
||||||
|
|
||||||
|
def encodeSize(val, length=None):
|
||||||
|
""" Encode an element size.
|
||||||
|
|
||||||
|
@param val: The size to encode. If `None`, the EBML 'unknown' size
|
||||||
|
will be returned (1 or `length` bytes, all bits 1).
|
||||||
|
@keyword length: An explicit length for the encoded size. If `None`,
|
||||||
|
the size will be encoded at the minimum length required.
|
||||||
|
@return: an encoded size for an EBML element.
|
||||||
|
@raise ValueError: raised if the length is invalid, or the length cannot
|
||||||
|
be encoded.
|
||||||
|
"""
|
||||||
|
if val is None:
|
||||||
|
# 'unknown' size: all bits 1.
|
||||||
|
length = 1 if (length is None or length == -1) else length
|
||||||
|
return b'\xff' * length
|
||||||
|
|
||||||
|
length = getLength(val) if (length is None or length == -1) else length
|
||||||
|
try:
|
||||||
|
prefix = LENGTH_PREFIXES[length]
|
||||||
|
return encodeUInt(val | prefix, length)
|
||||||
|
except (IndexError, TypeError):
|
||||||
|
raise ValueError("Cannot encode element size %s" % length)
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# --- Encoding
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
def encodeId(eid, length=None):
|
||||||
|
""" Encode an element ID.
|
||||||
|
|
||||||
|
@param eid: The EBML ID to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. A `ValueError`
|
||||||
|
will be raised if the length is too short to encode the value.
|
||||||
|
@return: The binary representation of ID, left-padded with ``0x00`` if
|
||||||
|
`length` is not `None`.
|
||||||
|
@return: The encoded version of the ID.
|
||||||
|
@raise ValueError: raised if length is less than one or more than 4.
|
||||||
|
"""
|
||||||
|
if length is not None:
|
||||||
|
if length < 1 or length > 4:
|
||||||
|
raise ValueError("Cannot encode an ID 0x%0x to length %d" %
|
||||||
|
(eid, length))
|
||||||
|
return encodeUInt(eid, length)
|
||||||
|
|
||||||
|
|
||||||
|
def encodeUInt(val, length=None):
|
||||||
|
""" Encode an unsigned integer.
|
||||||
|
|
||||||
|
@param val: The unsigned integer value to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. A `ValueError`
|
||||||
|
will be raised if the length is too short to encode the value.
|
||||||
|
@return: The binary representation of val as an unsigned integer,
|
||||||
|
left-padded with ``0x00`` if `length` is not `None`.
|
||||||
|
@raise ValueError: raised if val is longer than length.
|
||||||
|
"""
|
||||||
|
if isinstance(val, float):
|
||||||
|
fval, val = val, int(val)
|
||||||
|
if fval != val:
|
||||||
|
warnings.warn('encodeUInt: float value {} encoded as {}'.format(fval, val))
|
||||||
|
|
||||||
|
pad = b'\x00'
|
||||||
|
packed = _struct_uint64.pack(val).lstrip(pad) or pad
|
||||||
|
|
||||||
|
if length is None:
|
||||||
|
return packed
|
||||||
|
if len(packed) > length:
|
||||||
|
raise ValueError("Encoded length (%d) greater than specified length "
|
||||||
|
"(%d)" % (len(packed), length))
|
||||||
|
return packed.rjust(length, pad)
|
||||||
|
|
||||||
|
|
||||||
|
def encodeInt(val, length=None):
|
||||||
|
""" Encode a signed integer.
|
||||||
|
|
||||||
|
@param val: The signed integer value to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. A `ValueError`
|
||||||
|
will be raised if the length is too short to encode the value.
|
||||||
|
@return: The binary representation of val as a signed integer,
|
||||||
|
left-padded with either ```0x00`` (for positive values) or ``0xFF``
|
||||||
|
(for negative) if `length` is not `None`.
|
||||||
|
@raise ValueError: raised if val is longer than length.
|
||||||
|
"""
|
||||||
|
if isinstance(val, float):
|
||||||
|
fval, val = val, int(val)
|
||||||
|
if fval != val:
|
||||||
|
warnings.warn('encodeInt: float value {} encoded as {}'.format(fval, val))
|
||||||
|
|
||||||
|
if val >= 0:
|
||||||
|
pad = b'\x00'
|
||||||
|
packed = _struct_int64.pack(val).lstrip(pad) or pad
|
||||||
|
if packed[0] & 0b10000000:
|
||||||
|
packed = pad + packed
|
||||||
|
else:
|
||||||
|
pad = b'\xff'
|
||||||
|
packed = _struct_int64.pack(val).lstrip(pad) or pad
|
||||||
|
if not packed[0] & 0b10000000:
|
||||||
|
packed = pad + packed
|
||||||
|
|
||||||
|
if length is None:
|
||||||
|
return packed
|
||||||
|
if len(packed) > length:
|
||||||
|
raise ValueError("Encoded length (%d) greater than specified length "
|
||||||
|
"(%d)" % (len(packed), length))
|
||||||
|
return packed.rjust(length, pad)
|
||||||
|
|
||||||
|
|
||||||
|
def encodeFloat(val, length=None):
|
||||||
|
""" Encode a floating point value.
|
||||||
|
|
||||||
|
@param val: The floating point value to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. Must be
|
||||||
|
`None`, 0, 4, or 8; otherwise, a `ValueError` will be raised.
|
||||||
|
@return: The binary representation of val as a float, left-padded with
|
||||||
|
``0x00`` if `length` is not `None`.
|
||||||
|
@raise ValueError: raised if val not length 0, 4, or 8
|
||||||
|
"""
|
||||||
|
if length is None:
|
||||||
|
if val is None or val == 0.0:
|
||||||
|
return b''
|
||||||
|
else:
|
||||||
|
length = DEFAULT_FLOAT_SIZE
|
||||||
|
|
||||||
|
if length == 0:
|
||||||
|
return b''
|
||||||
|
if length == 4:
|
||||||
|
return _struct_float32.pack(val)
|
||||||
|
elif length == 8:
|
||||||
|
return _struct_float64.pack(val)
|
||||||
|
else:
|
||||||
|
raise ValueError("Cannot encode float of length %d; only 0, 4, or 8" %
|
||||||
|
length)
|
||||||
|
|
||||||
|
|
||||||
|
def encodeBinary(val, length=None):
|
||||||
|
""" Encode binary data.
|
||||||
|
|
||||||
|
@param val: A string or bytearray containing the data to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. A
|
||||||
|
`ValueError` will be raised if `length` is shorter than the
|
||||||
|
actual length of the binary data.
|
||||||
|
@return: The binary representation of value as binary data, left-padded
|
||||||
|
with ``0x00`` if `length` is not `None`.
|
||||||
|
@raise ValueError: raised if val is longer than length.
|
||||||
|
"""
|
||||||
|
if isinstance(val, str):
|
||||||
|
val = val.encode('utf_8')
|
||||||
|
elif val is None:
|
||||||
|
val = b''
|
||||||
|
|
||||||
|
if length is None:
|
||||||
|
return val
|
||||||
|
elif len(val) <= length:
|
||||||
|
return val.ljust(length, b'\x00')
|
||||||
|
else:
|
||||||
|
raise ValueError("Length of data (%d) exceeds specified length (%d)" %
|
||||||
|
(len(val), length))
|
||||||
|
|
||||||
|
|
||||||
|
def encodeString(val, length=None):
|
||||||
|
""" Encode an ASCII string.
|
||||||
|
|
||||||
|
@param val: The string (or bytearray) to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. Longer
|
||||||
|
strings will be truncated.
|
||||||
|
@keyword length: An explicit length for the encoded data. The result
|
||||||
|
will be truncated if the length is less than that of the original.
|
||||||
|
@return: The binary representation of val as a string, truncated or
|
||||||
|
left-padded with ``0x00`` if `length` is not `None`.
|
||||||
|
"""
|
||||||
|
if isinstance(val, str):
|
||||||
|
val = val.encode('ascii', 'replace')
|
||||||
|
|
||||||
|
if length is not None:
|
||||||
|
val = val[:length]
|
||||||
|
|
||||||
|
return encodeBinary(val.translate(STRING_CHARACTERS), length)
|
||||||
|
|
||||||
|
|
||||||
|
def encodeUnicode(val, length=None):
|
||||||
|
""" Encode a Unicode string.
|
||||||
|
|
||||||
|
@param val: The Unicode string to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. The result
|
||||||
|
will be truncated if the length is less than that of the original.
|
||||||
|
@return: The binary representation of val as a string, truncated or
|
||||||
|
left-padded with ``0x00`` if `length` is not `None`.
|
||||||
|
"""
|
||||||
|
val = val.encode('utf_8')
|
||||||
|
|
||||||
|
if length is not None:
|
||||||
|
val = val[:length]
|
||||||
|
|
||||||
|
return encodeBinary(val, length)
|
||||||
|
|
||||||
|
|
||||||
|
def encodeDate(val, length=None):
|
||||||
|
""" Encode a `datetime` object as an EBML date (i.e. nanoseconds since
|
||||||
|
2001-01-01T00:00:00).
|
||||||
|
|
||||||
|
@param val: The `datetime.datetime` object value to encode.
|
||||||
|
@keyword length: An explicit length for the encoded data. Must be
|
||||||
|
`None` or 8; otherwise, a `ValueError` will be raised.
|
||||||
|
@return: The binary representation of val as an 8-byte dateTime.
|
||||||
|
@raise ValueError: raised if the length of the input is not 8 bytes.
|
||||||
|
"""
|
||||||
|
if length is None:
|
||||||
|
length = 8
|
||||||
|
elif length != 8:
|
||||||
|
raise ValueError("Dates must be of length 8")
|
||||||
|
|
||||||
|
if val is None:
|
||||||
|
val = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
delta = val - datetime.datetime(2001, 1, 1, tzinfo=None)
|
||||||
|
nanoseconds = (delta.microseconds +
|
||||||
|
((delta.seconds + (delta.days * 86400)) * 1000000)) * 1000
|
||||||
|
return encodeInt(nanoseconds, length)
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
ebmlite.schemata: Submodule containing the default schema XML files. It doesn't
|
||||||
|
contain any code; it's just for storage. Making it a module makes it easy to
|
||||||
|
find.
|
||||||
|
|
||||||
|
For the definition of the `Schema` class, see `ebmlite.core`.
|
||||||
|
"""
|
||||||
239
lambda/cctv-people-rekognition/ebmlite/schemata/matroska.xml
Normal file
239
lambda/cctv-people-rekognition/ebmlite/schemata/matroska.xml
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Schema name="Matroska">
|
||||||
|
<MasterElement name="EBML" id="0x1A45DFA3" mandatory="1" multiple="1" minver="1">Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
|
||||||
|
<UIntegerElement name="EBMLVersion" id="0x4286" mandatory="1" default="1" minver="1">The version of EBML parser used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLReadVersion" id="0x42F7" mandatory="1" default="1" minver="1">The minimum EBML version a parser has to support to read this file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxIDLength" id="0x42F2" mandatory="1" default="4" minver="1">The maximum length of the IDs you'll find in this file (4 or less in Matroska).</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxSizeLength" id="0x42F3" mandatory="1" default="8" minver="1">The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.</UIntegerElement>
|
||||||
|
<StringElement name="DocType" id="0x4282" mandatory="1" default="matroska" minver="1">A string that describes the type of document that follows this EBML header. 'matroska' in our case or 'webm' for webm files.</StringElement>
|
||||||
|
<UIntegerElement name="DocTypeVersion" id="0x4287" mandatory="1" default="1" minver="1">The version of DocType interpreter used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DocTypeReadVersion" id="0x4285" mandatory="1" default="1" minver="1">The minimum DocType version an interpreter has to support to read this file.</UIntegerElement>
|
||||||
|
<BinaryElement name="Void" level="-1" id="0xEC" minver="1">Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.</BinaryElement>
|
||||||
|
<BinaryElement name="CRC-32" level="-1" id="0xBF" minver="1" webm="0">The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian</BinaryElement>
|
||||||
|
<MasterElement name="SignatureSlot" level="-1" id="0x1B538667" multiple="1" webm="0">Contain signature of some (coming) elements in the stream.
|
||||||
|
<UIntegerElement name="SignatureAlgo" id="0x7E8A" webm="0">Signature algorithm used (1=RSA, 2=elliptic).</UIntegerElement>
|
||||||
|
<UIntegerElement name="SignatureHash" id="0x7E9A" webm="0">Hash algorithm used (1=SHA1-160, 2=MD5).</UIntegerElement>
|
||||||
|
<BinaryElement name="SignaturePublicKey" id="0x7EA5" webm="0">The public key to use with the algorithm (in the case of a PKI-based signature).</BinaryElement>
|
||||||
|
<BinaryElement name="Signature" id="0x7EB5" webm="0">The signature of the data (until a new.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="SignatureElements" id="0x7E5B" webm="0">Contains elements that will be used to compute the signature.
|
||||||
|
<MasterElement name="SignatureElementList" id="0x7E7B" multiple="1" webm="0">A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: <i>Cluster|Block|BlockAdditional</i> means that the BlockAdditional of all Blocks in all Clusters is used for encryption.
|
||||||
|
<BinaryElement name="SignedElement" id="0x6532" multiple="1" webm="0">An element ID whose data will be used to compute the signature.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="Segment" id="0x18538067" mandatory="1" multiple="1" minver="1">This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment.
|
||||||
|
<MasterElement name="SeekHead" cppname="SeekHeader" id="0x114D9B74" multiple="1" minver="1">Contains the <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">position</a> of other level 1 elements.
|
||||||
|
<MasterElement name="Seek" cppname="SeekPoint" id="0x4DBB" mandatory="1" multiple="1" minver="1">Contains a single seek entry to an EBML element.
|
||||||
|
<BinaryElement name="SeekID" id="0x53AB" mandatory="1" minver="1">The binary ID corresponding to the element name.</BinaryElement>
|
||||||
|
<UIntegerElement name="SeekPosition" id="0x53AC" mandatory="1" minver="1">The <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">position</a> of the element in the segment in octets (0 = first level 1 element).</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Info" id="0x1549A966" mandatory="1" multiple="1" minver="1">Contains miscellaneous general information and statistics on the file.
|
||||||
|
<BinaryElement name="SegmentUID" id="0x73A4" minver="1" webm="0" range="not 0" bytesize="16">A randomly generated unique ID to identify the current segment between many others (128 bits).</BinaryElement>
|
||||||
|
<UnicodeElement name="SegmentFilename" id="0x7384" minver="1" webm="0">A filename corresponding to this segment.</UnicodeElement>
|
||||||
|
<BinaryElement name="PrevUID" id="0x3CB923" minver="1" webm="0" bytesize="16">A unique ID to identify the previous chained segment (128 bits).</BinaryElement>
|
||||||
|
<UnicodeElement name="PrevFilename" id="0x3C83AB" minver="1" webm="0">An escaped filename corresponding to the previous segment.</UnicodeElement>
|
||||||
|
<BinaryElement name="NextUID" id="0x3EB923" minver="1" webm="0" bytesize="16">A unique ID to identify the next chained segment (128 bits).</BinaryElement>
|
||||||
|
<UnicodeElement name="NextFilename" id="0x3E83BB" minver="1" webm="0">An escaped filename corresponding to the next segment.</UnicodeElement>
|
||||||
|
<BinaryElement name="SegmentFamily" id="0x4444" multiple="1" minver="1" webm="0" bytesize="16">A randomly generated unique ID that all segments related to each other must use (128 bits).</BinaryElement>
|
||||||
|
<MasterElement name="ChapterTranslate" id="0x6924" multiple="1" minver="1" webm="0">A tuple of corresponding ID used by chapter codecs to represent this segment.
|
||||||
|
<UIntegerElement name="ChapterTranslateEditionUID" id="0x69FC" multiple="1" minver="1" webm="0">Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment.</UIntegerElement>
|
||||||
|
<UIntegerElement name="ChapterTranslateCodec" id="0x69BF" mandatory="1" minver="1" webm="0">The <a href="http://www.matroska.org/technical/specs/index.html#ChapProcessCodecID">chapter codec</a> using this ID (0: Matroska Script, 1: DVD-menu).</UIntegerElement>
|
||||||
|
<BinaryElement name="ChapterTranslateID" id="0x69A5" mandatory="1" minver="1" webm="0">The binary value used to represent this segment in the chapter codec data. The format depends on the <a href="http://www.matroska.org/technical/specs/chapters/index.html#ChapProcessCodecID">ChapProcessCodecID</a> used.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
<UIntegerElement name="TimecodeScale" id="0x2AD7B1" mandatory="1" minver="1" default="1000000">Timecode scale in nanoseconds (1.000.000 means all timecodes in the segment are expressed in milliseconds).</UIntegerElement>
|
||||||
|
<FloatElement name="Duration" id="0x4489" minver="1" range="> 0">Duration of the segment (based on TimecodeScale).</FloatElement>
|
||||||
|
<DateElement name="DateUTC" id="0x4461" minver="1">Date of the origin of timecode (value 0), i.e. production date.</DateElement>
|
||||||
|
<UnicodeElement name="Title" id="0x7BA9" minver="1" webm="0">General name of the segment.</UnicodeElement>
|
||||||
|
<UnicodeElement name="MuxingApp" id="0x4D80" mandatory="1" minver="1">Muxing application or library ("libmatroska-0.4.3").</UnicodeElement>
|
||||||
|
<UnicodeElement name="WritingApp" id="0x5741" mandatory="1" minver="1">Writing application ("mkvmerge-0.3.3").</UnicodeElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Cluster" id="0x1F43B675" multiple="1" minver="1">The lower level element containing the (monolithic) Block structure.
|
||||||
|
<UIntegerElement name="Timecode" cppname="ClusterTimecode" id="0xE7" mandatory="1" minver="1">Absolute timecode of the cluster (based on TimecodeScale).</UIntegerElement>
|
||||||
|
<MasterElement name="SilentTracks" cppname="ClusterSilentTracks" id="0x5854" minver="1" webm="0">The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use.
|
||||||
|
<UIntegerElement name="SilentTrackNumber" cppname="ClusterSilentTrackNumber" id="0x58D7" multiple="1" minver="1" webm="0">One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<UIntegerElement name="Position" cppname="ClusterPosition" id="0xA7" minver="1" webm="0">The <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">Position</a> of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.</UIntegerElement>
|
||||||
|
<UIntegerElement name="PrevSize" cppname="ClusterPrevSize" id="0xAB" minver="1">Size of the previous Cluster, in octets. Can be useful for backward playing.</UIntegerElement>
|
||||||
|
<BinaryElement name="SimpleBlock" id="0xA3" multiple="1" minver="2" webm="1" divx="1">Similar to <a href="http://www.matroska.org/technical/specs/index.html#Block">Block</a> but without all the extra information, mostly used to reduced overhead when no extra feature is needed. (see <a href="http://www.matroska.org/technical/specs/index.html#simpleblock_structure">SimpleBlock Structure</a>)</BinaryElement>
|
||||||
|
<MasterElement name="BlockGroup" id="0xA0" multiple="1" minver="1">Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
|
||||||
|
</MasterElement>
|
||||||
|
<BinaryElement name="EncryptedBlock" id="0xAF" multiple="1" webm="0">Similar to <a href="http://www.matroska.org/technical/specs/index.html#SimpleBlock">SimpleBlock</a> but the data inside the Block are Transformed (encrypt and/or signed). (see <a href="http://www.matroska.org/technical/specs/index.html#encryptedblock_structure">EncryptedBlock Structure</a>)</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Tracks" id="0x1654AE6B" multiple="1" minver="1">A top-level block of information with many tracks described.
|
||||||
|
<MasterElement name="TrackEntry" id="0xAE" mandatory="1" multiple="1" minver="1">Describes a track with all elements.
|
||||||
|
<UIntegerElement name="TrackNumber" id="0xD7" mandatory="1" minver="1" range="not 0">The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number).</UIntegerElement>
|
||||||
|
<UIntegerElement name="TrackUID" id="0x73C5" mandatory="1" minver="1" range="not 0">A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="TrackType" id="0x83" mandatory="1" minver="1" range="1-254">A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).</UIntegerElement>
|
||||||
|
<UIntegerElement name="FlagEnabled" cppname="TrackFlagEnabled" id="0xB9" mandatory="1" minver="2" webm="1" default="1" range="0-1">Set if the track is used. (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="FlagDefault" cppname="TrackFlagDefault" id="0x88" mandatory="1" minver="1" default="1" range="0-1">Set if that track (audio, video or subs) SHOULD be used if no language found matches the user preference. (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="FlagForced" cppname="TrackFlagForced" id="0x55AA" mandatory="1" minver="1" default="0" range="0-1">Set if that track MUST be used during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind. (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="FlagLacing" cppname="TrackFlagLacing" id="0x9C" mandatory="1" minver="1" default="1" range="0-1">Set if the track may contain blocks using lacing. (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="MinCache" cppname="TrackMinCache" id="0x6DE7" mandatory="1" minver="1" webm="0" default="0">The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used.</UIntegerElement>
|
||||||
|
<UIntegerElement name="MaxCache" cppname="TrackMaxCache" id="0x6DF8" minver="1" webm="0">The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DefaultDuration" cppname="TrackDefaultDuration" id="0x23E383" minver="1" range="not 0">Number of nanoseconds (i.e. not scaled) per frame.</UIntegerElement>
|
||||||
|
<FloatElement name="TrackTimecodeScale" id="0x23314F" mandatory="1" minver="1" webm="0" default="1.0" range="> 0">The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).</FloatElement>
|
||||||
|
<IntegerElement name="TrackOffset" id="0x537F" webm="0" default="0">A value to add to the Block's Timecode. This can be used to adjust the playback offset of a track.</IntegerElement>
|
||||||
|
<UIntegerElement name="MaxBlockAdditionID" id="0x55EE" mandatory="1" minver="1" webm="0" default="0">The maximum value of <a href="http://www.matroska.org/technical/specs/index.html#BlockAddID">BlockAddID</a>. A value 0 means there is no <a href="http://www.matroska.org/technical/specs/index.html#BlockAdditions">BlockAdditions</a> for this track.</UIntegerElement>
|
||||||
|
<UnicodeElement name="Name" cppname="TrackName" id="0x536E" minver="1">A human-readable track name.</UnicodeElement>
|
||||||
|
<StringElement name="Language" cppname="TrackLanguage" id="0x22B59C" minver="1" default="eng">Specifies the language of the track in the <a href="http://www.matroska.org/technical/specs/index.html#languages">Matroska languages form</a>.</StringElement>
|
||||||
|
<StringElement name="CodecID" id="0x86" mandatory="1" minver="1">An ID corresponding to the codec, see the <a href="http://www.matroska.org/technical/specs/codecid/index.html">codec page</a> for more info.</StringElement>
|
||||||
|
<BinaryElement name="CodecPrivate" id="0x63A2" minver="1">Private data only known to the codec.</BinaryElement>
|
||||||
|
<UnicodeElement name="CodecName" id="0x258688" minver="1">A human-readable string specifying the codec.</UnicodeElement>
|
||||||
|
<UIntegerElement name="AttachmentLink" cppname="TrackAttachmentLink" id="0x7446" minver="1" webm="0" range="not 0">The UID of an attachment that is used by this codec.</UIntegerElement>
|
||||||
|
<UnicodeElement name="CodecSettings" id="0x3A9697" webm="0">A string describing the encoding setting used.</UnicodeElement>
|
||||||
|
<StringElement name="CodecInfoURL" id="0x3B4040" multiple="1" webm="0">A URL to find information about the codec used.</StringElement>
|
||||||
|
<StringElement name="CodecDownloadURL" id="0x26B240" multiple="1" webm="0">A URL to download about the codec used.</StringElement>
|
||||||
|
<UIntegerElement name="CodecDecodeAll" id="0xAA" mandatory="1" minver="2" webm="0" default="1" range="0-1">The codec can decode potentially damaged data (1 bit).</UIntegerElement>
|
||||||
|
<UIntegerElement name="TrackOverlay" id="0x6FAB" multiple="1" minver="1" webm="0">Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see <a href="http://www.matroska.org/technical/specs/index.html#SilentTracks">SilentTracks</a>) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CodecDelay" id="0x56AA" mandatory="1" minver="2" webm="0" default="1" range="0-1">CodecDelay is The codec-built-in delay in nanoseconds. This value MUST be subtracted from each block timestamp in order to get the actual timestamp. The value SHOULD be small so the muxing of tracks with the same actual timestamp are in the same Cluster.</UIntegerElement>
|
||||||
|
<MasterElement name="TrackTranslate" id="0x6624" multiple="1" minver="1" webm="0">The track identification for the given Chapter Codec.
|
||||||
|
<UIntegerElement name="TrackTranslateEditionUID" id="0x66FC" multiple="1" minver="1" webm="0">Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment.</UIntegerElement>
|
||||||
|
<UIntegerElement name="TrackTranslateCodec" id="0x66BF" mandatory="1" minver="1" webm="0">The <a href="http://www.matroska.org/technical/specs/index.html#ChapProcessCodecID">chapter codec</a> using this ID (0: Matroska Script, 1: DVD-menu).</UIntegerElement>
|
||||||
|
<BinaryElement name="TrackTranslateTrackID" id="0x66A5" mandatory="1" minver="1" webm="0">The binary value used to represent this track in the chapter codec data. The format depends on the <a href="http://www.matroska.org/technical/specs/index.html#ChapProcessCodecID">ChapProcessCodecID</a> used.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Video" cppname="TrackVideo" id="0xE0" minver="1">Video settings
|
||||||
|
<UIntegerElement name="FlagInterlaced" cppname="VideoFlagInterlaced" id="0x9A" mandatory="1" minver="2" webm="1" default="0" range="0-1">Set if the video is interlaced. (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="StereoMode" cppname="VideoStereoMode" id="0x53B8" minver="3" webm="1" default="0">Stereo-3D video mode (0: mono, 1: side by side (left eye is first), 2: top-bottom (right eye is first), 3: top-bottom (left eye is first), 4: checkboard (right is first), 5: checkboard (left is first), 6: row interleaved (right is first), 7: row interleaved (left is first), 8: column interleaved (right is first), 9: column interleaved (left is first), 10: anaglyph (cyan/red), 11: side by side (right eye is first), 12: anaglyph (green/magenta)) . There are some more details on <a href="http://www.matroska.org/technical/specs/notes.html#3D">3D support in the Specification Notes</a>.</UIntegerElement>
|
||||||
|
<UIntegerElement name="OldStereoMode" id="0x53B9" maxver="0" webm="0" divx="0">Bogus StereoMode value used in old versions of libmatroska. DO NOT USE. (0: mono, 1: right eye, 2: left eye, 3: both eyes).</UIntegerElement>
|
||||||
|
<UIntegerElement name="PixelWidth" cppname="VideoPixelWidth" id="0xB0" mandatory="1" minver="1" range="not 0">Width of the encoded video frames in pixels.</UIntegerElement>
|
||||||
|
<UIntegerElement name="PixelHeight" cppname="VideoPixelHeight" id="0xBA" mandatory="1" minver="1" range="not 0">Height of the encoded video frames in pixels.</UIntegerElement>
|
||||||
|
<UIntegerElement name="PixelCropBottom" cppname="VideoPixelCropBottom" id="0x54AA" minver="1" default="0">The number of video pixels to remove at the bottom of the image (for HDTV content).</UIntegerElement>
|
||||||
|
<UIntegerElement name="PixelCropTop" cppname="VideoPixelCropTop" id="0x54BB" minver="1" default="0">The number of video pixels to remove at the top of the image.</UIntegerElement>
|
||||||
|
<UIntegerElement name="PixelCropLeft" cppname="VideoPixelCropLeft" id="0x54CC" minver="1" default="0">The number of video pixels to remove on the left of the image.</UIntegerElement>
|
||||||
|
<UIntegerElement name="PixelCropRight" cppname="VideoPixelCropRight" id="0x54DD" minver="1" default="0">The number of video pixels to remove on the right of the image.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DisplayWidth" cppname="VideoDisplayWidth" id="0x54B0" minver="1" default="PixelWidth" range="not 0">Width of the video frames to display. The default value is only valid when <a href="http://www.matroska.org/technical/specs/index.html#DisplayUnit">DisplayUnit</a> is 0.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DisplayHeight" cppname="VideoDisplayHeight" id="0x54BA" minver="1" default="PixelHeight" range="not 0">Height of the video frames to display. The default value is only valid when <a href="http://www.matroska.org/technical/specs/index.html#DisplayUnit">DisplayUnit</a> is 0.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DisplayUnit" cppname="VideoDisplayUnit" id="0x54B2" minver="1" default="0">How DisplayWidth & DisplayHeight should be interpreted (0: pixels, 1: centimeters, 2: inches, 3: Display Aspect Ratio).</UIntegerElement>
|
||||||
|
<UIntegerElement name="AspectRatioType" cppname="VideoAspectRatio" id="0x54B3" minver="1" default="0">Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).</UIntegerElement>
|
||||||
|
<BinaryElement name="ColourSpace" cppname="VideoColourSpace" id="0x2EB524" minver="1" webm="0" bytesize="4">Same value as in AVI (32 bits).</BinaryElement>
|
||||||
|
<FloatElement name="GammaValue" cppname="VideoGamma" id="0x2FB523" webm="0" range="> 0">Gamma Value.</FloatElement>
|
||||||
|
<FloatElement name="FrameRate" cppname="VideoFrameRate" id="0x2383E3" range="> 0">Number of frames per second. <strong>Informational</strong> only.</FloatElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Audio" cppname="TrackAudio" id="0xE1" minver="1">Audio settings.
|
||||||
|
<FloatElement name="SamplingFrequency" cppname="AudioSamplingFreq" id="0xB5" mandatory="1" minver="1" default="8000.0" range="> 0">Sampling frequency in Hz.</FloatElement>
|
||||||
|
<FloatElement name="OutputSamplingFrequency" cppname="AudioOutputSamplingFreq" id="0x78B5" minver="1" default="Sampling Frequency" range="> 0">Real output sampling frequency in Hz (used for SBR techniques).</FloatElement>
|
||||||
|
<UIntegerElement name="Channels" cppname="AudioChannels" id="0x9F" mandatory="1" minver="1" default="1" range="not 0">Numbers of channels in the track.</UIntegerElement>
|
||||||
|
<BinaryElement name="ChannelPositions" cppname="AudioPosition" id="0x7D7B" webm="0">Table of horizontal angles for each successive channel, see <a href="http://www.matroska.org/technical/specs/index.html#channelposition">appendix</a>.</BinaryElement>
|
||||||
|
<UIntegerElement name="BitDepth" cppname="AudioBitDepth" id="0x6264" minver="1" range="not 0">Bits per sample, mostly used for PCM.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="TrackOperation" id="0xE2" minver="3" webm="0">Operation that needs to be applied on tracks to create this virtual track. For more details <a href="http://www.matroska.org/technical/specs/notes.html#TrackOperation">look at the Specification Notes</a> on the subject.
|
||||||
|
<MasterElement name="TrackCombinePlanes" id="0xE3" minver="3" webm="0">Contains the list of all video plane tracks that need to be combined to create this 3D track
|
||||||
|
<MasterElement name="TrackPlane" id="0xE4" mandatory="1" multiple="1" minver="3" webm="0">Contains a video plane track that need to be combined to create this 3D track
|
||||||
|
<UIntegerElement name="TrackPlaneUID" id="0xE5" mandatory="1" minver="3" webm="0" range="not 0">The trackUID number of the track representing the plane.</UIntegerElement>
|
||||||
|
<UIntegerElement name="TrackPlaneType" id="0xE6" mandatory="1" minver="3" webm="0">The kind of plane this track corresponds to (0: left eye, 1: right eye, 2: background).</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="TrackJoinBlocks" id="0xE9" minver="3" webm="0">Contains the list of all tracks whose Blocks need to be combined to create this virtual track
|
||||||
|
<UIntegerElement name="TrackJoinUID" id="0xED" mandatory="1" multiple="1" minver="3" webm="0" range="not 0">The trackUID number of a track whose blocks are used to create this virtual track.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<UIntegerElement name="TrickTrackUID" id="0xC0" divx="1"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/Smooth_FF_RW">DivX trick track extenstions</a></UIntegerElement>
|
||||||
|
<BinaryElement name="TrickTrackSegmentUID" id="0xC1" divx="1" bytesize="16"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/Smooth_FF_RW">DivX trick track extenstions</a></BinaryElement>
|
||||||
|
<UIntegerElement name="TrickTrackFlag" id="0xC6" divx="1" default="0"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/Smooth_FF_RW">DivX trick track extenstions</a></UIntegerElement>
|
||||||
|
<UIntegerElement name="TrickMasterTrackUID" id="0xC7" divx="1"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/Smooth_FF_RW">DivX trick track extenstions</a></UIntegerElement>
|
||||||
|
<BinaryElement name="TrickMasterTrackSegmentUID" id="0xC4" divx="1" bytesize="16"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/Smooth_FF_RW">DivX trick track extenstions</a></BinaryElement>
|
||||||
|
<MasterElement name="ContentEncodings" id="0x6D80" minver="1" webm="0">Settings for several content encoding mechanisms like compression or encryption.
|
||||||
|
<MasterElement name="ContentEncoding" id="0x6240" mandatory="1" multiple="1" minver="1" webm="0">Settings for one content encoding like compression or encryption.
|
||||||
|
<UIntegerElement name="ContentEncodingOrder" id="0x5031" mandatory="1" minver="1" webm="0" default="0">Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment.</UIntegerElement>
|
||||||
|
<UIntegerElement name="ContentEncodingScope" id="0x5032" mandatory="1" minver="1" webm="0" default="1" range="not 0">A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:<br/> 1 - all frame contents,<br/> 2 - the track's private data,<br/> 4 - the next ContentEncoding (next ContentEncodingOrder. Either the data inside ContentCompression and/or ContentEncryption)</UIntegerElement>
|
||||||
|
<UIntegerElement name="ContentEncodingType" id="0x5033" mandatory="1" minver="1" webm="0" default="0">A value describing what kind of transformation has been done. Possible values:<br/> 0 - compression,<br/> 1 - encryption</UIntegerElement>
|
||||||
|
<MasterElement name="ContentCompression" id="0x5034" minver="1" webm="0">Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking.
|
||||||
|
<UIntegerElement name="ContentCompAlgo" id="0x4254" mandatory="1" minver="1" webm="0" default="0">The compression algorithm used. Algorithms that have been specified so far are:<br/> 0 - zlib,<br/> <del>1 - bzlib,</del><br/> <del>2 - lzo1x</del><br/> 3 - Header Stripping</UIntegerElement>
|
||||||
|
<BinaryElement name="ContentCompSettings" id="0x4255" minver="1" webm="0">Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="ContentEncryption" id="0x5035" minver="1" webm="0">Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise.
|
||||||
|
<UIntegerElement name="ContentEncAlgo" id="0x47E1" minver="1" webm="0" default="0">The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:<br/> 1 - DES, 2 - 3DES, 3 - Twofish, 4 - Blowfish, 5 - AES</UIntegerElement>
|
||||||
|
<BinaryElement name="ContentEncKeyID" id="0x47E2" minver="1" webm="0">For public key algorithms this is the ID of the public key the the data was encrypted with.</BinaryElement>
|
||||||
|
<BinaryElement name="ContentSignature" id="0x47E3" minver="1" webm="0">A cryptographic signature of the contents.</BinaryElement>
|
||||||
|
<BinaryElement name="ContentSigKeyID" id="0x47E4" minver="1" webm="0">This is the ID of the private key the data was signed with.</BinaryElement>
|
||||||
|
<UIntegerElement name="ContentSigAlgo" id="0x47E5" minver="1" webm="0" default="0">The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:<br/> 1 - RSA</UIntegerElement>
|
||||||
|
<UIntegerElement name="ContentSigHashAlgo" id="0x47E6" minver="1" webm="0" default="0">The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:<br/> 1 - SHA1-160<br/> 2 - MD5</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Cues" id="0x1C53BB6B" minver="1">A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non <a href="http://www.matroska.org/technical/streaming/index.hmtl">"live" streams</a>.
|
||||||
|
<MasterElement name="CuePoint" id="0xBB" mandatory="1" multiple="1" minver="1">Contains all information relative to a seek point in the segment.
|
||||||
|
<UIntegerElement name="CueTime" id="0xB3" mandatory="1" minver="1">Absolute timecode according to the segment time base.</UIntegerElement>
|
||||||
|
<MasterElement name="CueTrackPositions" id="0xB7" mandatory="1" multiple="1" minver="1">Contain positions for different tracks corresponding to the timecode.
|
||||||
|
<UIntegerElement name="CueTrack" id="0xF7" mandatory="1" minver="1" range="not 0">The track for which a position is given.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CueClusterPosition" id="0xF1" mandatory="1" minver="1">The <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">position</a> of the Cluster containing the required Block.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CueBlockNumber" id="0x5378" minver="1" default="1" range="not 0">Number of the Block in the specified Cluster.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CueCodecState" id="0xEA" minver="2" webm="0" default="0">The <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">position</a> of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry.</UIntegerElement>
|
||||||
|
<MasterElement name="CueReference" id="0xDB" multiple="1" minver="2" webm="0">The Clusters containing the required referenced Blocks.
|
||||||
|
<UIntegerElement name="CueRefTime" id="0x96" mandatory="1" minver="2" webm="0">Timecode of the referenced Block.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CueRefCluster" id="0x97" mandatory="1" webm="0">The <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">Position</a> of the Cluster containing the referenced Block.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CueRefNumber" id="0x535F" webm="0" default="1" range="not 0">Number of the referenced Block of Track X in the specified Cluster.</UIntegerElement>
|
||||||
|
<UIntegerElement name="CueRefCodecState" id="0xEB" webm="0" default="0">The <a href="http://www.matroska.org/technical/specs/notes.html#Position_References">position</a> of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Attachments" id="0x1941A469" minver="1" webm="0">Contain attached files.
|
||||||
|
<MasterElement name="AttachedFile" id="0x61A7" mandatory="1" multiple="1" minver="1" webm="0">An attached file.
|
||||||
|
<UnicodeElement name="FileDescription" id="0x467E" minver="1" webm="0">A human-friendly name for the attached file.</UnicodeElement>
|
||||||
|
<UnicodeElement name="FileName" id="0x466E" mandatory="1" minver="1" webm="0">Filename of the attached file.</UnicodeElement>
|
||||||
|
<StringElement name="FileMimeType" id="0x4660" mandatory="1" minver="1" webm="0">MIME type of the file.</StringElement>
|
||||||
|
<BinaryElement name="FileData" id="0x465C" mandatory="1" minver="1" webm="0">The data of the file.</BinaryElement>
|
||||||
|
<UIntegerElement name="FileUID" id="0x46AE" mandatory="1" minver="1" webm="0" range="not 0">Unique ID representing the file, as random as possible.</UIntegerElement>
|
||||||
|
<BinaryElement name="FileReferral" id="0x4675" webm="0">A binary value that a track/codec can refer to when the attachment is needed.</BinaryElement>
|
||||||
|
<UIntegerElement name="FileUsedStartTime" id="0x4661" divx="1"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/World_Fonts">DivX font extension</a></UIntegerElement>
|
||||||
|
<UIntegerElement name="FileUsedEndTime" id="0x4662" divx="1"><a href="http://developer.divx.com/docs/divx_plus_hd/format_features/World_Fonts">DivX font extension</a></UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Chapters" id="0x1043A770" minver="1" webm="0">A system to define basic menus and partition data. For more detailed information, look at the <a href="http://www.matroska.org/technical/specs/chapters/index.html">Chapters Explanation</a>.
|
||||||
|
<MasterElement name="EditionEntry" id="0x45B9" mandatory="1" multiple="1" minver="1" webm="0">Contains all information about a segment edition.
|
||||||
|
<UIntegerElement name="EditionUID" id="0x45BC" minver="1" webm="0" range="not 0">A unique ID to identify the edition. It's useful for tagging an edition.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EditionFlagHidden" id="0x45BD" mandatory="1" minver="1" webm="0" default="0" range="0-1">If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks). (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="EditionFlagDefault" id="0x45DB" mandatory="1" minver="1" webm="0" default="0" range="0-1">If a flag is set (1) the edition should be used as the default one. (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="EditionFlagOrdered" id="0x45DD" minver="1" webm="0" default="0" range="0-1">Specify if the chapters can be defined multiple times and the order to play them is enforced. (1 bit)</UIntegerElement>
|
||||||
|
<MasterElement name="ChapterAtom" recursive="1" id="0xB6" mandatory="1" multiple="1" minver="1" webm="0">Contains the atom information to use as the chapter atom (apply to all tracks).
|
||||||
|
<UIntegerElement name="ChapterUID" id="0x73C4" mandatory="1" minver="1" webm="0" range="not 0">A unique ID to identify the Chapter.</UIntegerElement>
|
||||||
|
<UIntegerElement name="ChapterTimeStart" id="0x91" mandatory="1" minver="1" webm="0">Timecode of the start of Chapter (not scaled).</UIntegerElement>
|
||||||
|
<UIntegerElement name="ChapterTimeEnd" id="0x92" minver="1" webm="0">Timecode of the end of Chapter (timecode excluded, not scaled).</UIntegerElement>
|
||||||
|
<UIntegerElement name="ChapterFlagHidden" id="0x98" mandatory="1" minver="1" webm="0" default="0" range="0-1">If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks). (1 bit)</UIntegerElement>
|
||||||
|
<UIntegerElement name="ChapterFlagEnabled" id="0x4598" mandatory="1" minver="1" webm="0" default="1" range="0-1">Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter. (1 bit)</UIntegerElement>
|
||||||
|
<BinaryElement name="ChapterSegmentUID" id="0x6E67" minver="1" webm="0" range=">0" bytesize="16">A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used.</BinaryElement>
|
||||||
|
<BinaryElement name="ChapterSegmentEditionUID" id="0x6EBC" minver="1" webm="0" range="not 0" bytesize="16">The edition to play from the segment linked in ChapterSegmentUID.</BinaryElement>
|
||||||
|
<UIntegerElement name="ChapterPhysicalEquiv" id="0x63C3" minver="1" webm="0">Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see <a href="http://www.matroska.org/technical/specs/index.html#physical">complete list of values</a>.</UIntegerElement>
|
||||||
|
<MasterElement name="ChapterTrack" id="0x8F" minver="1" webm="0">List of tracks on which the chapter applies. If this element is not present, all tracks apply
|
||||||
|
<UIntegerElement name="ChapterTrackNumber" id="0x89" mandatory="1" multiple="1" minver="1" webm="0" range="not 0">UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="ChapterDisplay" id="0x80" multiple="1" minver="1" webm="0">Contains all possible strings to use for the chapter display.
|
||||||
|
<MasterElement name="ChapProcess" cppname="ChapterProcess" id="0x6944" multiple="1" minver="1" webm="0">Contains all the commands associated to the Atom.
|
||||||
|
<UIntegerElement name="ChapProcessCodecID" cppname="ChapterProcessCodecID" id="0x6955" mandatory="1" minver="1" webm="0" default="0">Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the <a href="http://www.matroska.org/technical/specs/chapters/index.html#dvd">DVD</a> command set is used. More codec IDs can be added later.</UIntegerElement>
|
||||||
|
<BinaryElement name="ChapProcessPrivate" cppname="ChapterProcessPrivate" id="0x450D" minver="1" webm="0">Some optional data attached to the ChapProcessCodecID information. <a href="http://www.matroska.org/technical/specs/chapters/index.html#dvd">For ChapProcessCodecID = 1</a>, it is the "DVD level" equivalent.</BinaryElement>
|
||||||
|
<MasterElement name="ChapProcessCommand" cppname="ChapterProcessCommand" id="0x6911" multiple="1" minver="1" webm="0">Contains all the commands associated to the Atom.
|
||||||
|
<UIntegerElement name="ChapProcessTime" cppname="ChapterProcessTime" id="0x6922" mandatory="1" minver="1" webm="0">Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter).</UIntegerElement>
|
||||||
|
<BinaryElement name="ChapProcessData" cppname="ChapterProcessData" id="0x6933" mandatory="1" minver="1" webm="0">Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. <a href="http://www.matroska.org/technical/specs/chapters/index.html#dvd">For ChapProcessCodecID = 1</a>, the data correspond to the binary DVD cell pre/post commands.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="Tags" id="0x1254C367" multiple="1" minver="1" webm="0">Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <a href="http://www.matroska.org/technical/specs/tagging/index.html">here.</a>
|
||||||
|
<MasterElement name="Tag" id="0x7373" mandatory="1" multiple="1" minver="1" webm="0">Element containing elements specific to Tracks/Chapters.
|
||||||
|
<MasterElement name="Targets" cppname="TagTargets" id="0x63C0" mandatory="1" minver="1" webm="0">Contain all UIDs where the specified meta data apply. It is empty to describe everything in the segment.
|
||||||
|
<UIntegerElement name="TargetTypeValue" cppname="TagTargetTypeValue" id="0x68CA" minver="1" webm="0" default="50">A number to indicate the logical level of the target (see <a href="http://www.matroska.org/technical/specs/tagging/index.html#targettypes">TargetType</a>).</UIntegerElement>
|
||||||
|
<StringElement name="TargetType" cppname="TagTargetType" id="0x63CA" minver="1" webm="0">An <strong>informational</strong> string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see <a href="http://www.matroska.org/technical/specs/tagging/index.html#targettypes">TargetType</a>).</StringElement>
|
||||||
|
<UIntegerElement name="TagTrackUID" id="0x63C5" multiple="1" minver="1" webm="0" default="0">A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment.</UIntegerElement>
|
||||||
|
<UIntegerElement name="TagEditionUID" id="0x63C9" multiple="1" minver="1" webm="0" default="0">A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment.</UIntegerElement>
|
||||||
|
<UIntegerElement name="TagChapterUID" id="0x63C4" multiple="1" minver="1" webm="0" default="0">A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment.</UIntegerElement>
|
||||||
|
<UIntegerElement name="TagAttachmentUID" id="0x63C6" multiple="1" minver="1" webm="0" default="0">A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="SimpleTag" cppname="TagSimple" recursive="1" id="0x67C8" mandatory="1" multiple="1" minver="1" webm="0">Contains general information about the target.
|
||||||
|
<UnicodeElement name="TagName" id="0x45A3" mandatory="1" minver="1" webm="0">The name of the Tag that is going to be stored.</UnicodeElement>
|
||||||
|
<StringElement name="TagLanguage" id="0x447A" mandatory="1" minver="1" webm="0" default="und">Specifies the language of the tag specified, in the <a href="http://www.matroska.org/technical/specs/index.html#languages">Matroska languages form</a>.</StringElement>
|
||||||
|
<UIntegerElement name="TagDefault" id="0x4484" mandatory="1" minver="1" webm="0" default="1" range="0-1">Indication to know if this is the default/original language to use for the given tag. (1 bit)</UIntegerElement>
|
||||||
|
<UnicodeElement name="TagString" id="0x4487" minver="1" webm="0">The value of the Tag.</UnicodeElement>
|
||||||
|
<BinaryElement name="TagBinary" id="0x4485" minver="1" webm="0">The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</Schema>
|
||||||
@ -0,0 +1,729 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Schema type="mide.ss.config" version="2" readversion="2">
|
||||||
|
<SchemaInfo>
|
||||||
|
<Author></Author>
|
||||||
|
<Description></Description>
|
||||||
|
</SchemaInfo>
|
||||||
|
|
||||||
|
<!-- Base EBML elements. Required. -->
|
||||||
|
<MasterElement name="EBML" id="0x1A45DFA3" mandatory="1" multiple="0" minver="1">Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
|
||||||
|
<UIntegerElement name="EBMLVersion" id="0x4286" multiple="0" mandatory="1" default="1" minver="1">The version of EBML parser used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLReadVersion" id="0x42F7" multiple="0" mandatory="1" default="1" minver="1">The minimum EBML version a parser has to support to read this file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxIDLength" id="0x42F2" multiple="0" mandatory="1" default="4" minver="1">The maximum length of the IDs you'll find in this file (4 or less in Matroska).</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxSizeLength" id="0x42F3" multiple="0" mandatory="1" default="8" minver="1">The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.</UIntegerElement>
|
||||||
|
<StringElement name="DocType" id="0x4282" multiple="0" mandatory="1" default="mide.ss.config" minver="1">A string that describes the type of document that follows this EBML header. 'mide' for Mide Instrumentation Data Exchange files.</StringElement>
|
||||||
|
<UIntegerElement name="DocTypeVersion" id="0x4287" multiple="0" mandatory="1" default="1" minver="1">The version of DocType interpreter used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DocTypeReadVersion" id="0x4285" multiple="0" mandatory="1" default="1" minver="1">The minimum DocType version an interpreter has to support to read this file.</UIntegerElement>
|
||||||
|
<BinaryElement name="Void" global="1" id="0xEC" multiple="1" minver="1">Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.</BinaryElement>
|
||||||
|
<BinaryElement name="CRC-32" global="1" id="0xBF" multiple="0" minver="1" webm="0">The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian</BinaryElement>
|
||||||
|
<MasterElement name="SignatureSlot" global="1" id="0x1B538667" multiple="1" webm="0">Contain signature of some (coming) elements in the stream.
|
||||||
|
<UIntegerElement name="SignatureAlgo" id="0x7E8A" multiple="0" webm="0">Signature algorithm used (1=RSA, 2=elliptic).</UIntegerElement>
|
||||||
|
<UIntegerElement name="SignatureHash" id="0x7E9A" multiple="0" webm="0">Hash algorithm used (1=SHA1-160, 2=MD5).</UIntegerElement>
|
||||||
|
<BinaryElement name="SignaturePublicKey" id="0x7EA5" multiple="0" webm="0">The public key to use with the algorithm (in the case of a PKI-based signature).</BinaryElement>
|
||||||
|
<BinaryElement name="Signature" id="0x7EB5" multiple="0" webm="0">The signature of the data (until a new.</BinaryElement>
|
||||||
|
<MasterElement name="SignatureElements" id="0x7E5B" multiple="0" webm="0">Contains elements that will be used to compute the signature.
|
||||||
|
<MasterElement name="SignatureElementList" id="0x7E7B" multiple="1" webm="0">A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: <i>Cluster|Block|BlockAdditional</i> means that the BlockAdditional of all Blocks in all Clusters is used for encryption.
|
||||||
|
<BinaryElement name="SignedElement" id="0x6532" multiple="1" webm="0">An element ID whose data will be used to compute the signature.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="ConfigUI" id="0x7777" mandatory="1" multiple="0">
|
||||||
|
<MasterElement name="Tab" id="0x4028" multiple="1" mandatory="1">
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
|
||||||
|
<MasterElement name="Group" global="1" id="0x4008" multiple="1">
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
NOTE: Any *Field can have any *Value type! The examples below show *Fields with matching
|
||||||
|
*Values, but that is just shorthand to make things less cumbersome for now.
|
||||||
|
-->
|
||||||
|
<MasterElement name="BooleanField" global="1" id="0x4000" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UIntegerElement name="BooleanValue" id="0x5100" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="UIntField" global="1" id="0x4001" multiple="1">
|
||||||
|
<IntegerField name="ConfigID" id="0x5001" multiple="0"></IntegerField>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="UIntMin" id="0x5111" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="UIntMax" id="0x5121" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<IntegerElement name="IntValue" id="0x5102" multiple="0"></IntegerElement>
|
||||||
|
<UIntegerElement name="UIntValue" id="0x5101" multiple="0"></UIntegerElement>
|
||||||
|
<FloatElement name="FloatValue" id="0x5103" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatGain" id="0x5203" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatOffset" id="0x5213" multiple="0"></FloatElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="IntField" global="1" id="0x4002" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<IntegerElement name="IntMin" id="0x5112" multiple="0"></IntegerElement>
|
||||||
|
<IntegerElement name="IntMax" id="0x5122" type="integer" multiple="0"></IntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="IntIncrement" id="0x5131" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<IntegerElement name="IntValue" id="0x5102" multiple="0"></IntegerElement>
|
||||||
|
<UIntegerElement name="UIntValue" id="0x5101" multiple="0"></UIntegerElement>
|
||||||
|
<FloatElement name="FloatValue" id="0x5103" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="FloatField" global="1" id="0x4003" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatMin" id="0x5113" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatMax" id="0x5123" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<IntegerElement name="IntValue" id="0x5102" multiple="0"></IntegerElement>
|
||||||
|
<UIntegerElement name="UIntValue" id="0x5101" multiple="0"></UIntegerElement>
|
||||||
|
<FloatElement name="FloatValue" id="0x5103" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="ASCIIField" global="1" id="0x4004" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="MaxLength" id="0x5124" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="TextLines" id="0x5134" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ASCIIValue" id="0x5104" multiple="0"></StringElement>
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="TextField" global="1" id="0x4005" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="MaxLength" id="0x5124" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="TextLines" id="0x5134" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" id="0x5105" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="EnumField" global="1" id="0x4007" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
<MasterElement name="EnumOption" global="1" id="0x4107" multiple="1">
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="BitField" global="1" id="0x4027" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<MasterElement name="EnumOption" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="DateTimeField" global="1" id="0x4022" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="UTCOffsetField" global="1" id="0x4023" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Note: The Check*Field elements should match the *Field elements. Any differences are probably
|
||||||
|
accidental.
|
||||||
|
-->
|
||||||
|
<MasterElement name="CheckBooleanField" global="1" id="0x4010" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UIntegerElement name="BooleanValue" id="0x5100" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckUIntField" global="1" id="0x4011" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="UIntMin" id="0x5111" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="UIntMax" id="0x5121" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckIntField" global="1" id="0x4012" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<IntegerElement name="IntMin" id="0x5112" multiple="0"></IntegerElement>
|
||||||
|
<IntegerElement name="IntMax" id="0x5122" type="integer" multiple="0"></IntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="IntIncrement" id="0x5131" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckFloatField" global="1" id="0x4013" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatMin" id="0x5113" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatMax" id="0x5123" multiple="0"></FloatElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckASCIIField" global="1" id="0x4014" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="MaxLength" id="0x5124" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="TextLines" id="0x5134" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckTextField" global="1" id="0x4015" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="MaxLength" id="0x5124" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="TextLines" id="0x5134" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="UIntValue" id="0x5101" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckEnumField" global="1" id="0x4017" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<MasterElement name="EnumOption" global="1" id="0x4107" multiple="1">
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckBitField" global="1" id="0x4037" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="Units" id="0x5025" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<MasterElement name="EnumOption" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckDateTimeField" global="1" id="0x4032" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckUTCOffsetField" global="1" id="0x4033" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="IntAccelerationField" global="1" id="0x4042" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckIntAccelerationField" global="1" id="0x4052" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="FloatAccelerationField" global="1" id="0x4043" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckFloatAccelerationField" global="1" id="0x4053" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="IntTemperatureField" global="1" id="0x4062" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckIntTemperatureField" global="1" id="0x4072" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="FloatTemperatureField" global="1" id="0x4063" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckFloatTemperatureField" global="1" id="0x4073" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
|
||||||
|
<MasterElement name="IntPressureField" global="1" id="0x4082" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckIntPressureField" global="1" id="0x4092" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="FloatPressureField" global="1" id="0x4083" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckFloatPressureField" global="1" id="0x4093" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="Label" id="0x5005" multiple="0"></UnicodeElement>
|
||||||
|
<UnicodeElement name="ToolTip" id="0x5015" multiple="0"></UnicodeElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0"></StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<StringElement name="DisplayFormat" id="0x5034" multiple="0"></StringElement>
|
||||||
|
<FloatElement name="FloatIncrement" id="0x5133" multiple="0"></FloatElement>
|
||||||
|
<UIntegerElement name="FloatDigits" id="0x5141" multiple="0"></UIntegerElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="HiddenField" global="1" id="0x040FF" multiple="1">
|
||||||
|
A 'field' for config items without individual UI widgets (e.g. created from multiple other fields)
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<StringElement name="DisableIf" id="0x5014" multiple="0">For hidden fields, this determines if the config item will be written.</StringElement>
|
||||||
|
<StringElement name="ValueFormat" id="0x5024" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" />
|
||||||
|
<StringElement name="ASCIIValue" />
|
||||||
|
<IntegerElement name="IntValue" />
|
||||||
|
<UIntegerElement name="UIntValue" />
|
||||||
|
<FloatElement name="FloatValue" />
|
||||||
|
<FloatElement name="FloatGain" />
|
||||||
|
<FloatElement name="FloatOffset" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="VerticalPadding" global="1" id="0x400F" multiple="1">
|
||||||
|
Special-case widget that creates a resizable vertical spacer, placing the fields following it on the bottom of the dialog.
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="CheckDriftButton" global="1" id="0x402F" multiple="0">
|
||||||
|
Special-case control that compares device time to system time.
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="ResetButton" global="1" id="404F" multiple="1">
|
||||||
|
Special-case control that resets all the siblings in its group or tab to their factory default values.
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
</MasterElement> <!-- Group -->
|
||||||
|
|
||||||
|
<MasterElement name="CheckGroup" global="1" id="0x4018" multiple="1">
|
||||||
|
<!-- All these children have already been defined, so they can be abbreviated -->
|
||||||
|
<UnicodeElement name="Label" />
|
||||||
|
<UnicodeElement name="ToolTip" />
|
||||||
|
<MasterElement name="BooleanField" />
|
||||||
|
<MasterElement name="UIntField" />
|
||||||
|
<MasterElement name="IntField" />
|
||||||
|
<MasterElement name="FloatField" />
|
||||||
|
<MasterElement name="ASCIIField" />
|
||||||
|
<MasterElement name="TextField" />
|
||||||
|
<MasterElement name="EnumField" />
|
||||||
|
<MasterElement name="DateTimeField" />
|
||||||
|
<MasterElement name="UTCOffsetField" />
|
||||||
|
<MasterElement name="CheckUIntField" />
|
||||||
|
<MasterElement name="CheckIntField" />
|
||||||
|
<MasterElement name="CheckFloatField" />
|
||||||
|
<MasterElement name="CheckASCIIField" />
|
||||||
|
<MasterElement name="CheckTextField" />
|
||||||
|
<MasterElement name="CheckEnumField" />
|
||||||
|
<MasterElement name="CheckDateTimeField" />
|
||||||
|
<MasterElement name="CheckUTCOffsetField" />
|
||||||
|
<MasterElement name="IntAccelerationField" />
|
||||||
|
<MasterElement name="CheckIntAccelerationField" />
|
||||||
|
<MasterElement name="FloatAccelerationField" />
|
||||||
|
<MasterElement name="CheckFloatAccelerationField" />
|
||||||
|
<MasterElement name="IntTemperatureField" />
|
||||||
|
<MasterElement name="CheckIntTemperatureField" />
|
||||||
|
<MasterElement name="FloatTemperatureField" />
|
||||||
|
<MasterElement name="CheckFloatTemperatureField" />
|
||||||
|
<MasterElement name="IntPressureField" />
|
||||||
|
<MasterElement name="CheckIntPressureField" />
|
||||||
|
<MasterElement name="FloatPressureField" />
|
||||||
|
<MasterElement name="CheckFloatPressureField" />
|
||||||
|
<MasterElement name="HiddenField" />
|
||||||
|
<MasterElement name="CheckDriftButton" />
|
||||||
|
<MasterElement name="VerticalPadding" />
|
||||||
|
<MasterElement name="ResetButton" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="BooleanField" />
|
||||||
|
<MasterElement name="UIntField" />
|
||||||
|
<MasterElement name="IntField" />
|
||||||
|
<MasterElement name="FloatField" />
|
||||||
|
<MasterElement name="ASCIIField" />
|
||||||
|
<MasterElement name="TextField" />
|
||||||
|
<MasterElement name="EnumField" />
|
||||||
|
<MasterElement name="DateTimeField" />
|
||||||
|
<MasterElement name="UTCOffsetField" />
|
||||||
|
<MasterElement name="CheckUIntField" />
|
||||||
|
<MasterElement name="CheckIntField" />
|
||||||
|
<MasterElement name="CheckFloatField" />
|
||||||
|
<MasterElement name="CheckASCIIField" />
|
||||||
|
<MasterElement name="CheckTextField" />
|
||||||
|
<MasterElement name="CheckEnumField" />
|
||||||
|
<MasterElement name="CheckDateTimeField" />
|
||||||
|
<MasterElement name="CheckUTCOffsetField" />
|
||||||
|
<MasterElement name="IntAccelerationField" />
|
||||||
|
<MasterElement name="CheckIntAccelerationField" />
|
||||||
|
<MasterElement name="FloatAccelerationField" />
|
||||||
|
<MasterElement name="CheckFloatAccelerationField" />
|
||||||
|
<MasterElement name="IntTemperatureField" />
|
||||||
|
<MasterElement name="CheckIntTemperatureField" />
|
||||||
|
<MasterElement name="FloatTemperatureField" />
|
||||||
|
<MasterElement name="CheckFloatTemperatureField" />
|
||||||
|
<MasterElement name="IntPressureField" />
|
||||||
|
<MasterElement name="CheckIntPressureField" />
|
||||||
|
<MasterElement name="FloatPressureField" />
|
||||||
|
<MasterElement name="CheckFloatPressureField" />
|
||||||
|
<MasterElement name="HiddenField" />
|
||||||
|
<MasterElement name="CheckDriftButton" />
|
||||||
|
<MasterElement name="VerticalPadding" />
|
||||||
|
<MasterElement name="ResetButton" />
|
||||||
|
|
||||||
|
</MasterElement> <!-- Tab -->
|
||||||
|
|
||||||
|
<!-- Special-case Tabs. These do no require child elements. -->
|
||||||
|
<MasterElement name="FactoryCalibrationTab" global="1" id="0x4A08" multiple="1"></MasterElement>
|
||||||
|
<MasterElement name="UserCalibrationTab" global="1" id="0x4A28" multiple="1"></MasterElement>
|
||||||
|
<MasterElement name="DeviceInfoTab" global="1" id="0x4A48" multiple="1"></MasterElement>
|
||||||
|
|
||||||
|
</MasterElement> <!-- ConfigUI -->
|
||||||
|
|
||||||
|
<MasterElement name="RecorderConfigurationList" id="0x18436668" global="1" multiple="0" mandatory="0">
|
||||||
|
<MasterElement name="RecorderConfigurationItem" id="0x5008" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0" />
|
||||||
|
<UIntegerElement name="BooleanValue" id="0x5100" multiple="0" />
|
||||||
|
<IntegerElement name="IntValue" id="0x5102" multiple="0" />
|
||||||
|
<UIntegerElement name="UIntValue" id="0x5101" multiple="0" />
|
||||||
|
<FloatElement name="FloatValue" id="0x5103" multiple="0" />
|
||||||
|
<StringElement name="ASCIIValue" id="0x5104" multiple="0" />
|
||||||
|
<UnicodeElement name="TextValue" id="0x5105" multiple="0" />
|
||||||
|
</MasterElement> <!-- RecorderConfigurationItem -->
|
||||||
|
</MasterElement> <!-- RecorderConfigurationList -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Attributes: a way to insert an arbitrary key/value into a structure, without revising (and potentially bloating) the schema itself. This data is typically non-critical.
|
||||||
|
Strictly speaking, this may be considered an abuse of EBML, but it is flexible and moderately clean.
|
||||||
|
-->
|
||||||
|
<MasterElement name="Attribute" global="1" id="0x6110" multiple="1" minver="2"> Container For arbitrary name/value attributes, allowing additional data without revising (and bloating) the schema. All of these elements are level -1, allowing an AttributeList to occur at any level, but should always be used at the relative levels implied below.
|
||||||
|
<UnicodeElement name="AttributeName" id="0x612f" multiple="0" minver="2"> Attribute name. Should always be child of Atrribute. </UnicodeElement>
|
||||||
|
<IntegerElement name="IntAttribute" id="0x6120" multiple="0" minver="2"> Integer Attribute. Should always be child of Atrribute. </IntegerElement>
|
||||||
|
<UIntegerElement name="UIntAttribute" id="0x6121" multiple="0" minver="2"> Unsigned integer Attribute. Should always be child of Atrribute. </UIntegerElement>
|
||||||
|
<FloatElement name="FloatAttribute" id="0x6122" multiple="0" minver="2"> Floating point Attribute. Should always be child of Atrribute. </FloatElement>
|
||||||
|
<StringElement name="StringAttribute" id="0x6123" multiple="0" minver="2"> ASCII String Attribute. Should always be child of Atrribute. </StringElement>
|
||||||
|
<DateElement name="DateAttribute" id="0x6124" multiple="0" minver="2"> Date Attribute. Should always be child of Atrribute. </DateElement>
|
||||||
|
<BinaryElement name="BinaryAttribute" id="0x6125" multiple="0" minver="2"> Binary Attribute. Should always be child of Atrribute. </BinaryElement>
|
||||||
|
<UnicodeElement name="UnicodeAttribute" id="0x6126" multiple="0" minver="2"> ASCII String Attribute. Should always be child of Atrribute. </UnicodeElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
</Schema>
|
||||||
385
lambda/cctv-people-rekognition/ebmlite/schemata/mide_ide.xml
Normal file
385
lambda/cctv-people-rekognition/ebmlite/schemata/mide_ide.xml
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Schema type="mide" version="2" readversion="2">
|
||||||
|
<!-- Base EBML elements. Required. -->
|
||||||
|
<MasterElement name="EBML" id="0x1A45DFA3" mandatory="1" multiple="0" minver="1">Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
|
||||||
|
<UIntegerElement name="EBMLVersion" id="0x4286" multiple="0" mandatory="1" default="1" minver="1">The version of EBML parser used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLReadVersion" id="0x42F7" multiple="0" mandatory="1" default="1" minver="1">The minimum EBML version a parser has to support to read this file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxIDLength" id="0x42F2" multiple="0" mandatory="1" default="4" minver="1">The maximum length of the IDs you'll find in this file (4 or less in Matroska).</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxSizeLength" id="0x42F3" multiple="0" mandatory="1" default="8" minver="1">The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.</UIntegerElement>
|
||||||
|
<StringElement name="DocType" id="0x4282" multiple="0" mandatory="1" default="mide" minver="1">A string that describes the type of document that follows this EBML header. 'mide' for Mide Instrumentation Data Exchange files.</StringElement>
|
||||||
|
<UIntegerElement name="DocTypeVersion" id="0x4287" multiple="0" mandatory="1" default="2" minver="1">The version of DocType interpreter used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DocTypeReadVersion" id="0x4285" multiple="0" mandatory="1" default="2" minver="1">The minimum DocType version an interpreter has to support to read this file.</UIntegerElement>
|
||||||
|
<BinaryElement name="Void" global="1" id="0xEC" multiple="1" minver="1">Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.</BinaryElement>
|
||||||
|
<BinaryElement name="CRC-32" global="1" id="0xBF" multiple="0" minver="1" webm="0">The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian</BinaryElement>
|
||||||
|
<MasterElement name="SignatureSlot" global="1" id="0x1B538667" multiple="1" webm="0">Contain signature of some (coming) elements in the stream.
|
||||||
|
<UIntegerElement name="SignatureAlgo" id="0x7E8A" multiple="0" webm="0">Signature algorithm used (1=RSA, 2=elliptic).</UIntegerElement>
|
||||||
|
<UIntegerElement name="SignatureHash" id="0x7E9A" multiple="0" webm="0">Hash algorithm used (1=SHA1-160, 2=MD5).</UIntegerElement>
|
||||||
|
<BinaryElement name="SignaturePublicKey" id="0x7EA5" multiple="0" webm="0">The public key to use with the algorithm (in the case of a PKI-based signature).</BinaryElement>
|
||||||
|
<BinaryElement name="Signature" id="0x7EB5" multiple="0" webm="0">The signature of the data (until a new.</BinaryElement>
|
||||||
|
<MasterElement name="SignatureElements" id="0x7E5B" multiple="0" webm="0">Contains elements that will be used to compute the signature.
|
||||||
|
<MasterElement name="SignatureElementList" id="0x7E7B" multiple="1" webm="0">A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: <i>Cluster|Block|BlockAdditional</i> means that the BlockAdditional of all Blocks in all Clusters is used for encryption.
|
||||||
|
<BinaryElement name="SignedElement" id="0x6532" multiple="1" webm="0">An element ID whose data will be used to compute the signature.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!-- Mide format global tags. Support required for all .mide schemas. -->
|
||||||
|
<!-- Since the generating device may be very memory-constrained, and these tags may be repeated periodically for streaming, we use a short (global) element ID and generally allow them at any level -->
|
||||||
|
<UIntegerElement name="SchemaID" global="1" id="0xFE" minver="1">Device EBML schema (aka 'tagset') hint. Points to a numeric schema ID defined at the receiving side.</UIntegerElement>
|
||||||
|
<BinaryElement name="Sync" global="1" id="0xFA" minver="1">Used to provide an arbitrary length sync word (for network / stream framing purposes) at any point in the stream.</BinaryElement>
|
||||||
|
<!--<BinaryElement name="Discontinuity" global="1" id="0xFD" minver="1">Probably replaced by a flag in individual datablocks at the Channel level.</BinaryElement> -->
|
||||||
|
<IntegerElement name="ElementTag" global="1" id="0xFC" minver="1">Arbitrary tag. Allow for separate opening and closing tags without knowing the length of the enclosed data in advance. I.e. instead of [tag len value=[subtag len... /]/], [tag len=0][subtags and contents][/tag]. Positive value corresponds to the corresponding ElementID as an opening tag; the corresponding negative value as the closing tag. Value -int_max for any int size is reserved.</IntegerElement>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
VR gen3 (Slam Stick / WVR) tags
|
||||||
|
===============================
|
||||||
|
|
||||||
|
Quick n dirty ElementID convention:
|
||||||
|
|
||||||
|
* Global tags and data that will be written frequently and during live recording will use Class A IDs (0x80 ~ 0xFF)
|
||||||
|
* The frequently-written data mainly includes (S)CDBs and any valid children of (S)CDB, such as timecode formats.
|
||||||
|
* Since the number of IDs is small, we'll make no convention about human-readability whatsoever and allocate fixed blocks of address space for specific types of element.
|
||||||
|
* Completely made-up convention: For any 16-element master tag reservation, the child tag reservation will begin at (master tag reservation base + 0x10).
|
||||||
|
0xFx (0xF0 ~ 0xFF): MIDE-defined global tags, except for SYNC, and reserved value (0xFF). No master tags.
|
||||||
|
0xAx (0xA0 ~ 0xAF): Datablock formats (ChannelDataBlock, SimpleChannelDataBlock). Child elements are 0xBX...
|
||||||
|
0xBx (0xB0 ~ 0xBE): Datablock child elements (mainly timestamp formats)
|
||||||
|
NOTE: 0xEC and 0xBF are defined by the EBML standard, so they don't conform to the above and we can't move them either.
|
||||||
|
|
||||||
|
|
||||||
|
Elements NOT written during live recording should use Class B or larger IDs.
|
||||||
|
These include the RecordingProperties (data describing the channels and any calibration, etc.), configfile elements, etc.
|
||||||
|
Matroska/WebM seem to follow a convention of making these larger IDs correspond to human-readable ASCII codes where possible. The address space is so large, it seems to make more sense than choosing them completely at random.
|
||||||
|
Class B (0x40 ~ 0x7F 0xXX) encoded first byte includes all of the alphanumerics (A-Z, a-z); so up to 2-byte readable codes are possible.
|
||||||
|
Class C (0x20 ~ 0x3F 0xXX 0xXX) encoded first byte is comprised entirely of printable characters, including space and the numerals.
|
||||||
|
Class D (0x10 ~ 0x1F 0xXX 0xXX 0xXX) encoded first byte comprises entirely NON-printable (control) characters. Most hexeditors will print them all as a dot, so the exact choice for the first byte is arbitrary.
|
||||||
|
|
||||||
|
TODO: Carve up each address space to prevent overlaps due to limitations in the python-ebml schema format (each ElementID can have only one name, datatype, etc.)
|
||||||
|
Class D (4-byte IDs):
|
||||||
|
[0x1A xx xx xx] 1A 45 DF A3 is used to contain EBML header
|
||||||
|
[0x18 xx xx xx] 18 53 80 67 is used in Matroska/Mide to define the start of a segment/session. Propose reserving [0x18 xx xx xx] for top-level elements within a file (Session, RecordingProperties, ...)
|
||||||
|
Class C (3-byte IDs):
|
||||||
|
None used yet?
|
||||||
|
Class B (2-byte IDs):
|
||||||
|
0x42 0xXX ("B") seem to be used in the EBML header itself.
|
||||||
|
0x43 0xXX ("C") we reserve for configuration-file children.
|
||||||
|
0x4B 0xXX ("K") we reserve for calibration elements.
|
||||||
|
0x52-0x53 0xXX ("R", "S") we reserve for represational data (RecordingProperties children).
|
||||||
|
0x54 0xXX ("T") we reserve for time-related data.
|
||||||
|
0x61 0xXX ("a") we reserve for arbitrary attributes, primarily related to drawing.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Elements of the RecordingProperties branch (representational/metadata) of the file -->
|
||||||
|
|
||||||
|
<MasterElement name="RecordingProperties" id="0x18526570" multiple="0" minver="1">Master element for the RecordingProperties branch of the file, if present.
|
||||||
|
<MasterElement name="RecorderInfo" id="0x5210" multiple="0" minver="1">Master element for the RecorderInfo items (ID, etc.).
|
||||||
|
<!-- NOTE: Several of the RecorderInfo tags are pulled from the Manifest and regurgitated here with a new tag ID to comply with this schema. -->
|
||||||
|
<UIntegerElement name="RecorderTypeUID" id="0x5211" multiple="0" minver="1">Unique Type ID assigned to each recorder variation. May be used to lookup remaining RecordingProperties when streaming.</UIntegerElement>
|
||||||
|
<UIntegerElement name="RecorderSerial" id="0x5212" multiple="0" minver="1">Recorder unique serial number</UIntegerElement>
|
||||||
|
<UIntegerElement name="RecorderSchemaID" id="0x5213" multiple="0" minver="1">ID referencing Schema in use by recorder</UIntegerElement>
|
||||||
|
<StringElement name="ProductName" id="0x5214" multiple="0" minver="1">Product designation of the recording device</StringElement>
|
||||||
|
<StringElement name="UserDeviceName" id="0x5215" multiple="0" minver="1">Text name of this recorder, if any. Probably set by the user.</StringElement>
|
||||||
|
<UIntegerElement name="HwRev" id="0x5216" multiple="0" minver="1">Hardware revision level</UIntegerElement>
|
||||||
|
<UIntegerElement name="FwRev" id="0x5217" multiple="0" minver="1">Firmware revision level</UIntegerElement>
|
||||||
|
<StringElement name="PartNumber" id="0x5218" multiple="0" minver="1">Device part .number. (text product identifier e.g. VR002-100-XYZ).</StringElement>
|
||||||
|
<UIntegerElement name="DateOfManufacture" id="0x5219" multiple="0" minver="1">Device .birthdate. (manufacture date) in UTC seconds since the Epoch.</UIntegerElement>
|
||||||
|
<StringElement name="HwCustomStr" id="0x521A" multiple="0" minver="1">Custom hardware identifier. Hardware is a custom version if present.</StringElement>
|
||||||
|
<StringElement name="FwCustomStr" id="0x521B" multiple="0" minver="1">Custom firmware build. Firmware is a custom build if present. Name should match FW branch/tag name as applicable for identification purposes, but is mainly present so FW updater can generate a warning if a custom build will be replaced by a standard one.</StringElement>
|
||||||
|
<StringElement name="FwRevStr" id="0x521C" multiple="0" minver="1">Firmware revision string.</StringElement>
|
||||||
|
<UIntegerElement name="UniqueChipID" id="0x521D" multiple="0" minver="1">The device CPU's factory-set unique ID.</UIntegerElement>
|
||||||
|
<StringElement name="McuType" id="0x521E" multiple="0" minver="1">Code indicating the type of CPU.</StringElement>
|
||||||
|
<StringElement name="BootloaderRevStr" id="0x521F" multiple="0" minver="1">Bootloader revision string.</StringElement>
|
||||||
|
<UIntegerElement name="BootloaderRev" id="0x5220" multiple="0" minver="1">Incrementing bootloader revision level</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<!-- SensorList and PlotList are optional; there are defaults that will be used. The use of defaults is all-or-nothing; the file should define all sensors or have no SensorList at all. -->
|
||||||
|
<MasterElement name="SensorList" id="0x5240" multiple="0" minver="2">Master element for the SensorList items
|
||||||
|
<MasterElement name="Sensor" id="0x5241" multiple="1" minver="2">Master element for a Sensor entry
|
||||||
|
<UIntegerElement name="SensorID" id="0x5242" multiple="0" minver="2">ID of a given Sensor entry; can be used to refer back to this sensor.</UIntegerElement>
|
||||||
|
<StringElement name="SensorName" id="0x5243" multiple="0" minver="2">Text name assigned to a sensor; probably the sensor make/model if present.</StringElement>
|
||||||
|
<UIntegerElement name="SensorBwLimitIDRef" id="0x5244" multiple="0" minver="2">Reference to a BwLimitList entry. If present, this discloses the usable bandwidth range of the sensor itself.</UIntegerElement>
|
||||||
|
<MasterElement name="TraceabilityData" id="0x5250" multiple="0" minver="2">Master element for any traceability data tied to the Sensor.
|
||||||
|
<StringElement name="SensorSerialNumber" id="0x5251" multiple="0" minver="2">Sensor manufacturer-supplied serial number, if any.</StringElement>
|
||||||
|
<!-- Child elements TBD... -->
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="ChannelList" id="0x5270" multiple="0" minver="2">Master element for a Channel entry
|
||||||
|
<MasterElement name="Channel" id="0x5271" multiple="1" minver="2">Master element for a Channel entry
|
||||||
|
<UIntegerElement name="ChannelID" id="0x5272" multiple="0" minver="2">ID of a given Channel entry; can be used to refer to this entry. Referenced by ChannelDataBlock/SimpleChannelDataBlock/etc.</UIntegerElement>
|
||||||
|
<StringElement name="ChannelName" id="0x5273" multiple="0" minver="2">Descriptive overall text name for this channel (e.g. "ADC").</StringElement>
|
||||||
|
<UIntegerElement name="ChannelCalibrationIDRef" id="0x5274" multiple="0" minver="2">Reference to a Calibration in CalibrationList.</UIntegerElement>
|
||||||
|
<StringElement name="ChannelFormat" id="0x5275" multiple="0" minver="2">Declaration of the format and representation of the (sub)channels' data. Modeled after Python Struct format strings, may be expanded at a later date. Currently limited to the expanded format-string format, i.e. [>HHHHH] rather than [>5H]. Whitespace and undefined formatting characters are ignored.</StringElement>
|
||||||
|
<StringElement name="ChannelParser" id="0x5276" multiple="0" minver="2">Name of the hardcoded parsing object, if known. Overrides ChannelFormat.</StringElement>
|
||||||
|
|
||||||
|
<StringElement name="TimeCodeScale" id="0x5277" multiple="0" minver="2">Channel seconds/tick. String represents a valid numeric expression, such as integer, decimal or ratio, e.g. '16262/32768'. Applies to all subsequent Abs timecodes. If not set, use channel default.</StringElement>
|
||||||
|
<UIntegerElement name="TimeCodeModulus" id="0x5278" multiple="0" minver="2">The modulus at which modulo timestamps for this channel roll over.</UIntegerElement>
|
||||||
|
<StringElement name="SampleRate" id="0x5279" multiple="0" minver="2">The samplerate for this channel, if known and fixed. String represents a valid numeric expression, such as integer, decimal or ratio, e.g. '32768/16262'. Element required if both starting and ending timecodes will ever be omitted for blocks in this channel.</StringElement>
|
||||||
|
<MasterElement name="SubChannel" id="0x52A0" multiple="1" minver="2">Master element for SensorSubChannels.
|
||||||
|
<IntegerElement name="SubChannelID" id="0x52A1" multiple="0" minver="2">ID of this SubChannel. Currently, SubChannelIDs must be sequential, starting from 0, for use with Slam Stick Lab.</IntegerElement>
|
||||||
|
<StringElement name="SubChannelName" id="0x52A2" multiple="0" minver="2">Display name of the subchannel, typically the axis name for multiaxis measurements (e.g. "X", "Yaw", etc.). Typically omitted if no display name is needed beyond a measurement label and units.</StringElement>
|
||||||
|
<UIntegerElement name="SubChannelCalibrationIDRef" id="0x52A3" multiple="0" minver="2">Reference to a Calibration in CalibrationList.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelBwLimitIDRef" id="0x52A4" multiple="0" minver="2">Reference to a BwLimitList entry. If present, this discloses any bandwidth limitations imposed for the acquisition channel, e.g. antialias or other filter settings. Note that the effective bandwidth is the lesser of the Sensor and SubChannel bandwidth!</UIntegerElement>
|
||||||
|
<StringElement name="SubChannelLabel" id="0x52A5" multiple="0" minver="2">General measurement type label for this subchannel (e.g. "Acceleration", "Rotation", "Temperature", etc.).</StringElement>
|
||||||
|
<UnicodeElement name="SubChannelUnits" id="0x52A6" multiple="0" minver="2">Text name of the engineering unit (e.g. "g") for this subchannel. Unicode characters such as degree or Greek symbols are allowed and encouraged.</UnicodeElement>
|
||||||
|
<UIntegerElement name="SubChannelRangeMin" id="0x52A7" multiple="0" minver="2">Minimum valid sensor value; may be used for data validation or initial axis scaling during display.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelRangeMax" id="0x52A8" multiple="0" minver="2">Maximum valid sensor value; may be used for data validation or initial axis scaling during display.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelSensorRef" id="0x52A9" multiple="0" minver="2">Reference to a Sensor ID. Allows the association between SubChannels and a physical sensor to be expressed.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelWarningRef" id="0x52AA" multiple="1" minver="2">Reference to a warning range, i.e. another sensor measuring something that affects the results of this one.</UIntegerElement>
|
||||||
|
<IntegerElement name="SubChannelVisibility" id="0x52AB" multiple="0" minver="2">Allow data channels to specify a visibility level; could have different visibility levels for e.g. 'advanced', 'on request' (channels which are not normally useful on their own, e.g. IRIG stream), 'hidden' (internal diagnostic or other dirty laundry).</IntegerElement>
|
||||||
|
<BinaryElement name="SubChannelPlotColor" id="0x52AC" multiple="0" minver="2">The RGB values of the default plotting color (3 bytes).</BinaryElement>
|
||||||
|
<StringElement name="SubChannelUnitsName" id="0x5304" multiple="0" minver="2">Text name of the sensor output units.</StringElement>
|
||||||
|
<UIntegerElement name="SubChannelUnitsXRef" id="0x5305" multiple="0" minver="2">Reference to a numbered, standardized SI unit.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="PlotList" id="0x5300" multiple="0" minver="2">Master element for the PlotList items. It works like a virtual Channel, with Plots being its Subchannels.
|
||||||
|
<MasterElement name="Plot" id="0x5301" multiple="1" minver="2">Master element for a Plot entry
|
||||||
|
<!-- NOTE: ElementIDs/names are intentionally replicated between SubChannel and Plot children. -->
|
||||||
|
<IntegerElement name="SubChannelID" id="0x52A1" multiple="0" minver="2">SubChannelID</IntegerElement>
|
||||||
|
<StringElement name="SubChannelName" id="0x52A2" multiple="0" minver="2">Text name of the subchannel (e.g. axis?).</StringElement>
|
||||||
|
<UIntegerElement name="SubChannelCalibrationIDRef" id="0x52A3" multiple="0" minver="2">Reference to a Calibration in CalibrationList.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelRangeMin" id="0x52A7" multiple="0" minver="2">Minimum valid sensor value.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelRangeMax" id="0x52A8" multiple="0" minver="2">Maximum valid sensor value.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelWarningRef" id="0x52AA" multiple="1" minver="2">Reference to a warning range, i.e. another sensor measuring something that affects the results of this one</UIntegerElement>
|
||||||
|
<IntegerElement name="SubChannelVisibility" id="0x52AB" multiple="0" minver="2">Allow data channels to specify a visibility level; could have different visibility levels for e.g. 'advanced', 'on request' (channels which are not normally useful on their own, e.g. IRIG stream), 'hidden' (internal diagnostic or other dirty laundry).</IntegerElement>
|
||||||
|
<BinaryElement name="SubChannelPlotColor" id="0x52AC" multiple="0" minver="2">The RGB values of the default plotting color (3 bytes).</BinaryElement>
|
||||||
|
<StringElement name="SubChannelUnitsName" id="0x5304" multiple="0" minver="2">Text name of the sensor output units.</StringElement>
|
||||||
|
<UIntegerElement name="SubChannelUnitsXRef" id="0x5305" multiple="0" minver="2">Reference to a numbered, standardized SI unit.</UIntegerElement>
|
||||||
|
<!--SubChannelBwLimitIDRef applicable here? -->
|
||||||
|
<!-- Elements unique to Plot -->
|
||||||
|
<StringElement name="SubChannelAxisName" id="0x5302" multiple="0" minver="2">Text name of the axis(?).</StringElement>
|
||||||
|
<UIntegerElement name="SubChannelAxisXRef" id="0x5303" multiple="0" minver="2">Reference to an Axis.</UIntegerElement>
|
||||||
|
<StringElement name="SubChannelUnitsName" id="0x5304" multiple="0" minver="2">Text name of the sensor output units.</StringElement>
|
||||||
|
<UIntegerElement name="SubChannelUnitsXRef" id="0x5305" multiple="0" minver="2">Reference to a numbered, standardized SI unit.</UIntegerElement>
|
||||||
|
|
||||||
|
<MasterElement name="PlotSource" id="0x5320" multiple="1" minver="2">List of channels/subchannels used by this Plot
|
||||||
|
<IntegerElement name="PlotChannelRef" id="0x5221" multiple="0" minver="2">SubChannelID</IntegerElement>
|
||||||
|
<IntegerElement name="PlotSubChannelRef" id="0x5222" multiple="0" minver="2">SubChannelID</IntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="WarningList" id="0x5360" multiple="0" minver="2"> "Idiot Light" warnings for sensors that are inaccurate in certain conditions
|
||||||
|
<MasterElement name="Warning" id="0x5361" multiple="1" minver="2">
|
||||||
|
<IntegerElement name="WarningID" id="0x5362" multiple="0" minver="2">Warning ID, referenced by Subchannels. </IntegerElement>
|
||||||
|
<IntegerElement name="WarningChannelRef" id="0x5363" multiple="0" minver="2">ChannelID</IntegerElement>
|
||||||
|
<IntegerElement name="WarningSubChannelRef" id="0x5364" multiple="0" minver="2">SubChannelID</IntegerElement>
|
||||||
|
<FloatElement name="WarningRangeMin" id="0x5365" multiple="0" minver="2">Minimum valid value. Note: this is in real, post-converted units, not raw channel units. </FloatElement>
|
||||||
|
<FloatElement name="WarningRangeMax" id="0x5366" multiple="0" minver="2">Maximum valid value. Note: this is in real, post-converted units, not raw channel units. </FloatElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="BwLimitList" id="0x5380" multiple="0" minver="2"> List of bandwidth constraints on sensors and data channels. These will (on a Sensor) provide the intrinsic sensor limits if known, or (on a data channel) provide information about any user-configured limits, e.g. AA filter cutoffs. This data is mandatory for sensor fusion, otherwise optional.
|
||||||
|
<MasterElement name="BwLimit" id="0x5381" multiple="1" minver="2">
|
||||||
|
<IntegerElement name="BwLimitID" id="0x5382" multiple="0" minver="2">Limit entry ID, referenced by Sensors or (Sub)channels. </IntegerElement>
|
||||||
|
<StringElement name="LowerCutoff" id="0x5383" multiple="0" minver="2">Lower cutoff frequency.</StringElement>
|
||||||
|
<StringElement name="LowerRolloff" id="0x5384" multiple="0" minver="2">Lower rolloff, in dB/decade.</StringElement>
|
||||||
|
<StringElement name="UpperCutoff" id="0x5385" multiple="0" minver="2">Upper cutoff frequency.</StringElement>
|
||||||
|
<StringElement name="UpperRolloff" id="0x5386" multiple="0" minver="2">Upper rolloff, in dB/decade.</StringElement>
|
||||||
|
<StringElement name="GroupDelay" id="0x5387" multiple="0" minver="2">Group delay, in channel ticks (channels/subchannels only).</StringElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<!--
|
||||||
|
Attributes: a way to insert an arbitrary key/value into a structure, without revising (and potentially bloating) the schema itself. This data is typically non-critical.
|
||||||
|
Strictly speaking, this may be considered an abuse of EBML, but it is flexible and moderately clean.
|
||||||
|
-->
|
||||||
|
<MasterElement name="Attribute" global="1" id="0x6110" multiple="1" minver="2"> Container For arbitrary name/value attributes, allowing additional data without revising (and bloating) the schema. All of these elements are level -1, allowing an AttributeList to occur at any level, but should always be used at the relative levels implied below.
|
||||||
|
<UnicodeElement name="AttributeName" id="0x612f" multiple="0" minver="2"> Attribute name. Should always be child of Atrribute. </UnicodeElement>
|
||||||
|
<IntegerElement name="IntAttribute" id="0x6120" multiple="0" minver="2"> Integer Attribute. Should always be child of Atrribute. </IntegerElement>
|
||||||
|
<UIntegerElement name="UIntAttribute" id="0x6121" multiple="0" minver="2"> Unsigned integer Attribute. Should always be child of Atrribute. </UIntegerElement>
|
||||||
|
<FloatElement name="FloatAttribute" id="0x6122" multiple="0" minver="2"> Floating point Attribute. Should always be child of Atrribute. </FloatElement>
|
||||||
|
<StringElement name="StringAttribute" id="0x6123" multiple="0" minver="2"> ASCII String Attribute. Should always be child of Atrribute. </StringElement>
|
||||||
|
<DateElement name="DateAttribute" id="0x6124" multiple="0" minver="2"> Date Attribute. Should always be child of Atrribute. </DateElement>
|
||||||
|
<BinaryElement name="BinaryAttribute" id="0x6125" multiple="0" minver="2"> Binary Attribute. Should always be child of Atrribute. </BinaryElement>
|
||||||
|
<UnicodeElement name="UnicodeAttribute" id="0x6126" multiple="0" minver="2"> ASCII String Attribute. Should always be child of Atrribute. </UnicodeElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Calibration entries. Any dynamically-generated data pertaining to plotting or syncing channels will eventually be written here. These are stored top-level to minimize the memory footprint of any device-side reprocessing of these entries.
|
||||||
|
|
||||||
|
Each calibration entry in the CalibrationList will be either a UnivariatePolynomial OR a BivariatePolynomial. The level-2 entries below are actually shared between these master types.
|
||||||
|
In the case of a BivariatePolynomial, the BivariateChannelIDRef and BivariateSubChannelIDRef are mandatory.
|
||||||
|
|
||||||
|
The calibration entry types and their coefficient blocks specify an n-degree univariate or bivariate polynomial, respectively.
|
||||||
|
|
||||||
|
Univariate: Evaluated in the form y = sum( A(x-xref)^n ) for all n, where y is the output sample, A is the coefficient, x is the input sample, xref is the reference value, n is the degree of each term and the terms are expressed from highest to lowest degree.
|
||||||
|
Examples:
|
||||||
|
2 coef block [A B]: y = A(x-xref) + B
|
||||||
|
4 coef block [A B C D]: y = A(x-xref)^3 + B(x-xref)^2 + C(x-xref) + D
|
||||||
|
|
||||||
|
Bivariate: Evaluated in the form y = sum( A(x-xref)^m(y-yref)^n ) for all m and n, where:
|
||||||
|
y is the output sample,
|
||||||
|
A is the coefficient,
|
||||||
|
x,y are the input samples for this and the 2nd channel,
|
||||||
|
xref,yref are the reference values for this and the 2nd channel,
|
||||||
|
m,n are the degrees of the x and y terms.
|
||||||
|
Ordering here is a little trickier: for any degree, a term must exist for every permutation the degrees 0~n for x and y, e.g. x^2y^2, x^2y, xy^2, xy, ...
|
||||||
|
Terms are expressed from highest to lowest total degree (m*n), with terms of equal degree sorted by degree of the 1st channel term (for example, x^2y^1 would come before x^1y^2), assuming the 1st channel term is always expressed first.
|
||||||
|
Due to the permutation, legal coefficient block lengths are those which satisfy the number of permuted terms, or generally, nCoefs=(degree+1)^2 . (FIXME: CHECKME: Valid up to 2nd order; have not checked further...)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
degree 0: 1 coef block [A] (trivial case for completeness): y = A(x-xref)^0(y-yref)^0 = A
|
||||||
|
degree 1: 4 coef block [A B C D]: A(x-xref)(y-yref) + B(x-xref) + C(y-yref) + D
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Rules: This should appear after the Recording properties element (more specifically, after SensorList and PlotList), if one exists and the Bivariate polynomials reference non-default (sub)channels. -->
|
||||||
|
|
||||||
|
<MasterElement name="CalibrationList" id="0x4B00" multiple="0" minver="1">Master element for the CalibrationList items
|
||||||
|
<MasterElement name="UnivariatePolynomial" id="0x4B01" multiple="1" minver="1">Master element for a univariate (1-variable) calibration entry
|
||||||
|
<UIntegerElement name="CalID" id="0x4B03" mandatory="1" multiple="0" minver="1">ID of this entry, referenced by SensorList.</UIntegerElement>
|
||||||
|
<FloatElement name="CalReferenceValue" id="0x4B04" multiple="0" minver="1">Reference value for this channel</FloatElement>
|
||||||
|
<FloatElement name="PolynomialCoef" id="0x4B08" multiple="1" minver="1">Univariate or bivariate polynomial coefficient in canonical order</FloatElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="BivariatePolynomial" id="0x4B02" multiple="1" minver="1">Master element for a bivariate (2-variable) calibration entry
|
||||||
|
<UIntegerElement name="CalID" id="0x4B03" mandatory="1" multiple="0" minver="1">ID of this entry, referenced by SensorList.</UIntegerElement>
|
||||||
|
<FloatElement name="CalReferenceValue" id="0x4B04" multiple="0" minver="1">Reference value for this channel</FloatElement>
|
||||||
|
<FloatElement name="BivariateCalReferenceValue" id="0x4B05" multiple="0" minver="1">Reference value for the 2nd channel when using bivariate calibration</FloatElement>
|
||||||
|
<UIntegerElement name="BivariateChannelIDRef" id="0x4B06" multiple="0" minver="1">Channel ID of the channel to be used as the 2nd parameter for bivariate compensation</UIntegerElement>
|
||||||
|
<UIntegerElement name="BivariateSubChannelIDRef" id="0x4B07" multiple="0" minver="1">SubChannel ID of the subchannel to be used as the 2nd parameter for bivariate compensation</UIntegerElement>
|
||||||
|
<FloatElement name="PolynomialCoef" id="0x4B08" multiple="1" minver="1">Univariate or bivariate polynomial coefficient in canonical order</FloatElement>
|
||||||
|
</MasterElement>
|
||||||
|
<UIntegerElement name="CalibrationDate" id="0x4B20" multiple="0" minver="1">Date of factory calibration, in UTC seconds</UIntegerElement>
|
||||||
|
<UIntegerElement name="CalibrationExpiry" id="0x4B22" multiple="0" minver="1">Date of factory calibration expiration, in UTC seconds</UIntegerElement>
|
||||||
|
<UIntegerElement name="CalibrationSerialNumber" id="0x4B21" multiple="0" minver="1">Mide-assigned serial # for a specific (re)calibration procedure. Can be used to lookup calibration conditions and exact facility / test stand used, etc.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!-- Elements of a configuration file -->
|
||||||
|
<!-- This is stuck into the main schema for convenience, but really it has no relation. If the configfile were to be attached to a recording as metadata, it would be as a binary blob - the 'Level's need not be consistent with the recording file. -->
|
||||||
|
<MasterElement name="RecorderConfiguration" id="0x18436667" multiple="0" minver="1">Master element for the RecorderConfiguration block, if present. This allows a mechanism to attach the actual recorder configuration to the file as metadata, and (hacky) use the same schema document to parse output files and write configuration files. It is expected that by necessity, children of RecorderConfiguration will be recorder-specific.
|
||||||
|
<MasterElement name="SSXBasicRecorderConfiguration" id="0x4300" multiple="0" minver="1">Master element for the SSX-specific RecorderConfiguration block. This and its subelements are specific to the basic Slam Stick X.
|
||||||
|
<UIntegerElement name="SampleFreq" id="0x4310" multiple="0" minver="1">Sampling frequency for the high speed channel(s) in Hz.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AAFilterCornerFreq" id="0x4311" multiple="0" minver="1">Antialias filter corner frequency for the high speed channel(s) in Hz. Omit to let the device choose based on the sampling rate. Enter 0 to disable (bypass) AA filter. (Beware: diagnostic usage only; calibration will be invalid in this state.)</UIntegerElement>
|
||||||
|
<UIntegerElement name="OSR" id="0x4312" multiple="0" minver="1">Oversampling ratio. Allowed values are 1 or power-of-2 integer in the range from 16 to 4096. Omit to let the device choose based on the sampling rate.</UIntegerElement>
|
||||||
|
<IntegerElement name="UTCOffset" id="0x4313" multiple="0" minver="1">UTC offset in seconds. May be used for entering local timezone offset. Sign follows the normal convention for specifying timezone offsets, e.g. "UTC-5" (-5*3600) to display the local time as 5 hours earlier than UTC. This value is only used to convert the internal UTC time to local time to generate the FAT16/32 file timestamp, which is expected to be in local time (sans any DST offset, which is added by the host system). If the device clock is directly set in local time for any reason, this field should be 0.</IntegerElement>
|
||||||
|
<UIntegerElement name="PlugPolicy" id="0x4314" multiple="0" minver="2">Defines device behavior in response to an unexpected plug-in (or other power/data connection) during recording. Current options are 0 (exit immediately), 1 (exit after current recording finishes - HW default), or 2 (ignore - require button presses to terminate recording mode).</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="SSXTriggerConfiguration" id="0x4340" multiple="0" minver="1">Master element for SSX trigger configuration.
|
||||||
|
<UIntegerElement name="WakeTimeUTC" id="0x4350" multiple="0" minver="1">Absolute, calendar time in UTC at which to arm/start the recorder. WakeTimeUTC and PreRecordDelay are mutually exclusive.</UIntegerElement>
|
||||||
|
<UIntegerElement name="PreRecordDelay" id="0x4351" multiple="0" minver="1">Delay in seconds before the start of recording (or trigger arming). WakeTimeUTC and PreRecordDelay are mutually exclusive.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AutoRearm" id="0x4352" multiple="0" minver="1">Automatically rearm at end of triggered recording (do not require button press)</UIntegerElement>
|
||||||
|
<UIntegerElement name="RecordingTime" id="0x4353" multiple="0" minver="1">Time in seconds to record for when triggered. Omit to impose no limit.</UIntegerElement>
|
||||||
|
<!-- Some IDs reserved for specifying activity behavior, e.g. restart the RecordingTime as long as something interesting is happening -->
|
||||||
|
<MasterElement name="Trigger" id="0x4360" multiple="1" minver="1">Master element for a generic trigger.
|
||||||
|
<IntegerElement name="TriggerChannel" id="0x4361" multiple="0" minver="1">The sensor channel used as the trigger. Mandatory for all Triggers.</IntegerElement>
|
||||||
|
<IntegerElement name="TriggerSubChannel" id="0x4362" multiple="0" minver="1">The sensor subchannel used by the trigger.</IntegerElement>
|
||||||
|
<IntegerElement name="TriggerWindowLo" id="0x4363" multiple="0" minver="1">Lower value of trigger window in native units.</IntegerElement>
|
||||||
|
<IntegerElement name="TriggerWindowHi" id="0x4364" multiple="0" minver="1">Upper value of trigger window in native units.</IntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="SSXChannelConfiguration" id="0x43A0" multiple="1" minver="2">Master element for channel-specific configuration elements.
|
||||||
|
<UIntegerElement name="ConfigChannel" id="0x43A1" mandatory="1" multiple="0" minver="2">Channel to apply these configuration elements to.</UIntegerElement>
|
||||||
|
<UIntegerElement name="ChannelSampleFreq" id="0x43A2" multiple="0" minver="2">Sampling frequency for this channel in Hz, if supported by device. Unsupported rates will be coerced to nearest supported rate.</UIntegerElement>
|
||||||
|
<UIntegerElement name="SubChannelEnableMap" id="0x43A3" multiple="0" minver="2">Bitmap indicating enabled/disabled SubChannels, if supported by device. If only Channel-level enable/disable supported by this channel, any nonzero value will enable the entire Channel.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="RecorderUserData" id="0x43F0" multiple="0" minver="1">The user-defined properties of the recorder, not used by the recorder itself.
|
||||||
|
<UnicodeElement name="RecorderName" id="0x43F1" multiple="0" minver="1">The user-defined name of the recorder</UnicodeElement>
|
||||||
|
<UnicodeElement name="RecorderDesc" id="0x43F2" multiple="0" minver="1">The user-defined description of/notes on the recorder</UnicodeElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<MasterElement name="RecorderConfigurationList" id="0x18436668" global="1" multiple="0" mandatory="0">
|
||||||
|
New configuration data: pairs of config IDs and values. Duplicated from
|
||||||
|
the config_ui.xml schema, rather than attempting to parse one element
|
||||||
|
with a different schema.
|
||||||
|
<MasterElement name="RecorderConfigurationItem" id="0x5008" multiple="1">
|
||||||
|
<UIntegerElement name="ConfigID" id="0x5001" multiple="0"></UIntegerElement>
|
||||||
|
<UIntegerElement name="BooleanValue" id="0x5100" multiple="0"></UIntegerElement>
|
||||||
|
<IntegerElement name="IntValue" id="0x5102" multiple="0"></IntegerElement>
|
||||||
|
<UIntegerElement name="UIntValue" id="0x5101" multiple="0"></UIntegerElement>
|
||||||
|
<FloatElement name="FloatValue" id="0x5103" multiple="0"></FloatElement>
|
||||||
|
<StringElement name="ASCIIValue" id="0x5104" multiple="0"></StringElement>
|
||||||
|
<UnicodeElement name="TextValue" id="0x5105" multiple="0"></UnicodeElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!-- Exported configuration data. Also not part of an IDE, but here for convenience. -->
|
||||||
|
<MasterElement name="ExportedConfigurationData" id="0x18436669" multiple="0" mandatory="0">
|
||||||
|
Exported configuration data. Also contains device information and
|
||||||
|
CONFIG.UI for compatibility.
|
||||||
|
<MasterElement name="RecorderConfigurationList" />
|
||||||
|
<MasterElement name="RecordingProperties" />
|
||||||
|
<BinaryElement name="ConfigUI" id="0x7777" mandatory="0" multiple="0">
|
||||||
|
Actually from the CONFIG.UI schema. Read as binary in this schema,
|
||||||
|
then parsed with the other one.
|
||||||
|
</BinaryElement>
|
||||||
|
<MasterElement name="Attribute" />
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!-- Elements of the actual recording(s) (Session branch(es) of the file) -->
|
||||||
|
|
||||||
|
<MasterElement name="Session" id="0x18538067" multiple="1" minver="1">This element contains all other top-level (level 1) elements. A session basically comprises one 'recording' and may contain any number of individual channels. A typical file should consist of one segment (mostly because we won't really know its length in advance).</MasterElement>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Children of Session. However, since we may implement Session tags as being completely optional / nonexistent / polite suggestion on the reader side, take 'Level' with a grain of salt...
|
||||||
|
|
||||||
|
Some notes on timecode stuff
|
||||||
|
|
||||||
|
Timecoding is vitally important to most data acquisition, so it's OK if these proliferate a bit...
|
||||||
|
TimecodeScales should be set *per channel*, and may use the default value ONLY if none declared for the channel!
|
||||||
|
For WVR / Slam Stick we'll use TimeBaseUTC, TimeCodeAbs, TimeCodeAbsMod. Any other timecode formats effectively don't exist until we need them...
|
||||||
|
TimeBase: A base time (Unix time stamp in seconds) applied in the Session. All future session timecodes are an offset from the last known TimeBase. Most likely a file will contain only a single TimeBase element at the start of the session.
|
||||||
|
TimeCode: Offset from the TimeBase; its scale is defined by a TimeCodeScale element on a per-channel basis (with 32768 ticks/sec as a default). The possible permutations are...
|
||||||
|
Start/End: The TimeCode may correspond to the first sample in the block (start) or the last sample in the block (end).
|
||||||
|
Abs/AbsMod/(Relative): The TimeCode may be absolute, modulo, or relative (not implemented). Absolute shall increase monotonically for the duration of the session and must be sized large enough to express any reasonably expected session length. Modulo timecodes are allowed to roll over at the UINT_MAX for a given number of octets.
|
||||||
|
In a typical application, the Session would start with a single TimeBase (probably overflows of a device's internal high-res clock, in seconds), immediately followed by a Session-level TimeCodeAbs(Mod) with the sub-second count from the same oscillator, captured at the same time as the seconds (to avoid any possible unhandled rollovers between the TimeBase and the first sample from individual channels), or a dummy zero-length *ChannelDataBlock for each channel if Session-level TimeCodes not allowed.
|
||||||
|
|
||||||
|
Rules for generated files / streams
|
||||||
|
|
||||||
|
General
|
||||||
|
The file/stream MAY contain tags not recognized by the viewer (e.g. for internal debugging or defined after a given viewer / schema version was released). The viewer is expected to skip the unrecognized data without crashing. The viewer MAY inform the user that unrecognized data is present, but isn't required to.
|
||||||
|
Files may contain any global tag defined by the EBML standard, including VOID tags, at any document level.
|
||||||
|
Length-encoded EBML data may encode length data using any number of bytes equal or greater than the minimum required to encode the value. For example, a length of 1 may be encoded as 0x81 or 0x4001 or 0x200001, etc. ("Gassy" encodings may be used by the device to enforce memory alignment or consume slack bytes in a fixed buffer allocation, etc.)
|
||||||
|
If the Schema or RecorderProperties are unavailable for any reason, the reader MAY substitute its best guess for this data, such as a default copy based on some signal (device ID, etc.). No guaranteees can be made about the ability of a given reader to parse a given file if these data are not present in the file. In the case of a Stream, the reader is free to ignore/discard all data received before both a SYNC tag and a reference to a valid Schema/RecordingProperties are received. (Really, there are no guarantees about anyone receiving or parsing the streamed data, period.)
|
||||||
|
For the time being, devices SHALL NOT write files consisting of multiple sessions, and must assume any representation of sessions in the file may be ignored. For the time being (forseeable future), the reader MAY define child elements of Session (datablocks, etc.) to actually be siblings.
|
||||||
|
|
||||||
|
Rules for channels
|
||||||
|
|
||||||
|
General
|
||||||
|
Although expressed as a signed type, channels written by the device must be positive. Negative channel numbers are reserved (may be used by the reader for e.g. virtual channels consisting of processed data).
|
||||||
|
Channel numbering is arbitrary and is not necessarily consecutive.
|
||||||
|
The device MAY list channels in its RecorderProperties that have no datablocks associated with them. (The channel information may be hardcoded and channels may appear for sensors that are disabled in the current configuration or aren't populated on the device.)
|
||||||
|
The device SHOULD NOT generate datablocks for channels that do not have a corresponding entry in RecorderProperties. The behavior of the reader upon encountering such channels is undefined. (A nice response would be to silently ignore them, but it could just crash, and it's your own damn fault, dear firmware writer.)
|
||||||
|
|
||||||
|
Rules for datablocks
|
||||||
|
|
||||||
|
General
|
||||||
|
The device MAY (and often will) use different block types for different channels. Most commonly, full CDBs for data taken at high rates and/or precise timing requirements, and a 'Simple' blocktype (e.g. SCDB) for low-rate channels consisting of only a few or single samples. The device MAY mix block types in a single channel. (It probably won't, but is not expressly forbidden from doing so.)
|
||||||
|
As a point of clarification, if the device mixes blocktypes in a channel, the types MAY use different timecode formats, and modulo timecodes may have different moduli. The usual rules below still apply - the device may not 'lose' rollovers.
|
||||||
|
|
||||||
|
Datablock timecoding rules:
|
||||||
|
General
|
||||||
|
The timecode scale, representation and modulus is allowed to differ between channnels, so accounting for rollovers in modulo timestamps should be handled per-channel.
|
||||||
|
Where modulo timestamps are used, the device MUST NOT allow more than one rollover period to elapse between blocks in a given channel. In the case of long gaps between datablocks in a channel, the device MUST either write a datablock using a timecode format large enough to correctly represent the time elapsed, OR write one or more dummy (zero-sample) datablocks such that all rollovers are "caught up" before the next datablock sample.
|
||||||
|
If a Start and End timecode are provided, the Start timecode is the time of the first sample in the block; the End timecode is the time of the last sample in the block. For formats that provide only a single timecode, it corresponds to the first sample in the block.
|
||||||
|
|
||||||
|
Modulo timecodes and mixing of timecodes
|
||||||
|
The modulus is defined as the exact value *at* which a given value is equal to zero. In other words, it is one more than the maximum possible remainder value. For example, a 16-bit timecode with a maximum legal value of 0xFFFF has a modulus of 0x10000.
|
||||||
|
A modulo timecode has rolled over if the current value is LESS THAN the previous value. Given two consecutive datablocks with identical modulo timecode 'n', no time has elapsed between them. Given two blocks with timecodes n and n-1, respectively, the maximum allowable time for the timecode's modulus has elapsed.
|
||||||
|
|
||||||
|
Timecode ordering
|
||||||
|
Timecodes in the datablocks written for a given channel MUST be monotonically increasing. That is, the device MUST NOT write a datablock for a given channel with an earlier timecode than an already written datablock for that channel. It can be safely assumed that a decrease in any modulo timecode represents a rollover, and any decrease in a non-modulo absolute timecode is an error condition. (This is a litter stricter than Matroska, which allows out-of-order timecodes as long as elements referencing another element's timecode come after said referenced element. The monotonicity requirement is to allow random-access seeking time ranges within the files.)
|
||||||
|
However, datablocks are NOT required to be monotonically increasing ACROSS channels.
|
||||||
|
In other words, given an output such as: <datablock1 ch=1> <datablock2 ch=2> <datablock3 ch=1>, datablock2 may have an earlier timecode than datablock1 (because they are different channels), but datablock3 may not (same channel).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<UIntegerElement name="TimeBaseUTC" id="0x5462" multiple="1" minver="1">Session time base value in Unix Time. If present, used as the base for all future timecodes in the session.</UIntegerElement>
|
||||||
|
|
||||||
|
<BinaryElement name="SimpleChannelDataBlock" id="0xA0" multiple="1" minver="1">This element contains potentially-channelized instrumentation data in a minimalist format. Its mandatory, fixed-length header includes a 2-byte modulo timecode (scaled to the channel's TimecodeScale, default of 1/32768 sec) and a 1-byte integer ChannelID.</BinaryElement>
|
||||||
|
|
||||||
|
<MasterElement name="ChannelDataBlock" id="0xA1" multiple="1" minver="1">This element contains child elements including instrumentation data associated to a channel. This is used for e.g. binding a timestamp(s) and/or metadata to a specific multi-sample block of sensor data, which may be written asynchronously with respect to other channels' data (i.e. multiplexed).
|
||||||
|
<IntegerElement name="ChannelIDRef" id="0xB0" multiple="0" minver="1">Child of ChannelDataBlock: the channel this data is associated with</IntegerElement>
|
||||||
|
<UIntegerElement name="ChannelFlags" id="0xB1" multiple="0" minver="1">Child of ChannelDataBlock: optional flags to indicate datablock features such as discontinuity</UIntegerElement>
|
||||||
|
<BinaryElement name="ChannelDataPayload" id="0xB2" multiple="0" minver="1">Child of ChannelDataBlock: the actual channel data samples. If there are multiple subchannels, for each sample point, the sample for each subchannel will be written consecutively (i.e. [sc0 sc1 sc2] [sc0 sc1 sc2]).</BinaryElement>
|
||||||
|
<UIntegerElement name="StartTimeCodeAbs" id="0xB8" multiple="0" minver="1">Absolute timecode as an offset from the session TimeBase. The timecode resolution is given by the channel's TimeCodeScale.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EndTimeCodeAbs" id="0xB9" multiple="0" minver="1">Absolute timecode as an offset from the session TimeBase. The timecode resolution is given by the channel's TimeCodeScale.</UIntegerElement>
|
||||||
|
<UIntegerElement name="StartTimeCodeAbsMod" id="0xBA" multiple="0" minver="1">Absolute start timecode as an offset from session TimeBase, mod TimeCodeModulus. Allows for a short, rolling-over (but still absolute) code. The timecode resolution is given by the channel's TimeCodeScale.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EndTimeCodeAbsMod" id="0xBB" multiple="0" minver="1">Absolute end timecode as an offset from session TimeBase, mod TimeCodeModulus. Allows for a short, rolling-over (but still absolute) code. The timecode resolution is given by the channel's TimeCodeScale.</UIntegerElement>
|
||||||
|
<BinaryElement name="ChannelDataMinMeanMax" id="0xBC" multiple="0" minver="1" precache="1">Statistical data for this block's payload consisting of 3 datapoints (min, mean, max) per subchannel. They are organized as [[sc0min] [sc1min] [sc2min] ...] [[sc0mean] [sc1mean] [sc2mean] ...] [[sc0max] [sc1max] [sc2max] ...]. The format and representation of the stat data exactly matches that of the input samples; that is, if the input samples are uint16_t, each stat entry is also a uint16_t.</BinaryElement>
|
||||||
|
<UIntegerElement name="MediaWriteLatency" id="0xBE" multiple="0" minver="2" precache="0">Super-optional diagnostic element indicating the latency between data acquisition and transfer to the output media. The exact meaning of this value is device-dependent, but may serve as a general indicator of excess activity load, retransmission or congestion (for transmission media) or media wear (for recording media).</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</Schema>
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Schema type="mide" version="2" readversion="2">
|
||||||
|
<!-- Base EBML elements. Required. -->
|
||||||
|
<MasterElement name="EBML" id="0x1A45DFA3" mandatory="1" multiple="0" minver="1">Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
|
||||||
|
<UIntegerElement name="EBMLVersion" id="0x4286" multiple="0" mandatory="1" default="1" minver="1">The version of EBML parser used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLReadVersion" id="0x42F7" multiple="0" mandatory="1" default="1" minver="1">The minimum EBML version a parser has to support to read this file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxIDLength" id="0x42F2" multiple="0" mandatory="1" default="4" minver="1">The maximum length of the IDs you'll find in this file (4 or less in Matroska).</UIntegerElement>
|
||||||
|
<UIntegerElement name="EBMLMaxSizeLength" id="0x42F3" multiple="0" mandatory="1" default="8" minver="1">The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.</UIntegerElement>
|
||||||
|
<StringElement name="DocType" id="0x4282" multiple="0" mandatory="1" default="mide" minver="1">A string that describes the type of document that follows this EBML header. 'mide' for Mide Instrumentation Data Exchange files.</StringElement>
|
||||||
|
<UIntegerElement name="DocTypeVersion" id="0x4287" multiple="0" mandatory="1" default="2" minver="1">The version of DocType interpreter used to create the file.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DocTypeReadVersion" id="0x4285" multiple="0" mandatory="1" default="2" minver="1">The minimum DocType version an interpreter has to support to read this file.</UIntegerElement>
|
||||||
|
<BinaryElement name="Void" global="1" id="0xEC" multiple="1" minver="1">Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.</BinaryElement>
|
||||||
|
<BinaryElement name="CRC-32" global="1" id="0xBF" multiple="0" minver="1" webm="0">The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian</BinaryElement>
|
||||||
|
<MasterElement name="SignatureSlot" global="1" id="0x1B538667" multiple="1" webm="0">Contain signature of some (coming) elements in the stream.
|
||||||
|
<UIntegerElement name="SignatureAlgo" id="0x7E8A" multiple="0" webm="0">Signature algorithm used (1=RSA, 2=elliptic).</UIntegerElement>
|
||||||
|
<UIntegerElement name="SignatureHash" id="0x7E9A" multiple="0" webm="0">Hash algorithm used (1=SHA1-160, 2=MD5).</UIntegerElement>
|
||||||
|
<BinaryElement name="SignaturePublicKey" id="0x7EA5" multiple="0" webm="0">The public key to use with the algorithm (in the case of a PKI-based signature).</BinaryElement>
|
||||||
|
<BinaryElement name="Signature" id="0x7EB5" multiple="0" webm="0">The signature of the data (until a new.</BinaryElement>
|
||||||
|
<MasterElement name="SignatureElements" id="0x7E5B" multiple="0" webm="0">Contains elements that will be used to compute the signature.
|
||||||
|
<MasterElement name="SignatureElementList" id="0x7E7B" multiple="1" webm="0">A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: <i>Cluster|Block|BlockAdditional</i> means that the BlockAdditional of all Blocks in all Clusters is used for encryption.
|
||||||
|
<BinaryElement name="SignedElement" id="0x6532" multiple="1" webm="0">An element ID whose data will be used to compute the signature.</BinaryElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!-- Mide format global tags. Support required for all .mide schemas. -->
|
||||||
|
<!-- Since the generating device may be very memory-constrained, and these tags may be repeated periodically for streaming, we use a short (global) element ID and generally allow them at any level -->
|
||||||
|
<UIntegerElement name="SchemaID" global="1" id="0xFE" minver="1">Device EBML schema (aka 'tagset') hint. Points to a numeric schema ID defined at the receiving side.</UIntegerElement>
|
||||||
|
<BinaryElement name="Sync" global="1" id="0xFA" minver="1">Used to provide an arbitrary length sync word (for network / stream framing purposes) at any point in the stream.</BinaryElement>
|
||||||
|
<!--<BinaryElement name="Discontinuity" global="1" id="0xFD" minver="1">Probably replaced by a flag in individual datablocks at the Channel level.</BinaryElement> -->
|
||||||
|
<IntegerElement name="ElementTag" global="1" id="0xFC" minver="1">Arbitrary tag. Allow for separate opening and closing tags without knowing the length of the enclosed data in advance. I.e. instead of [tag len value=[subtag len... /]/], [tag len=0][subtags and contents][/tag]. Positive value corresponds to the corresponding ElementID as an opening tag; the corresponding negative value as the closing tag. Value -int_max for any int size is reserved.</IntegerElement>
|
||||||
|
|
||||||
|
<!-- Slam Stick X Manifest tags -->
|
||||||
|
|
||||||
|
<!-- NOTES: -->
|
||||||
|
<!-- This is completely independent from the portable instrumentation schema. Under normal circumstances, nobody should have to read this data directly. It is internal housekeeping for the recording device. -->
|
||||||
|
|
||||||
|
<!-- Overall USERPAGE structure -->
|
||||||
|
<!-- The USERPAGE is 2KBytes (0x0000 ~ 0x07FF) and will store the complete Manifest and calibration entries. -->
|
||||||
|
<!-- 0x0000: (uint16-le) offset to start of Manifest -->
|
||||||
|
<!-- 0x0002: (uint16-le) length of Manifest -->
|
||||||
|
<!-- 0x0004: (uint16-le) offset to start of factory calibration info -->
|
||||||
|
<!-- 0x0006: (uint16-le) length of factory cal block -->
|
||||||
|
<!-- 0x0008: (uint16-le) offset to start of canned RecordingProperties -->
|
||||||
|
<!-- 0x000A: (uint16-le) length of canned RecordingProperties -->
|
||||||
|
<!-- (0x000C ~ 0x000F reserved) -->
|
||||||
|
<!-- 0x0010: Recommended starting offset of Manifest. If the number of unique sections, indicated by offset/length entries as above, exceeds this value, division between section offset/length entries and first payload will be determined by the offset of the Manifest. -->
|
||||||
|
|
||||||
|
<!-- Manifest structure -->
|
||||||
|
<!-- Consists of a DeviceManifest master tag and its subelements as shown below. The SystemInfo and Battery entries are required. -->
|
||||||
|
<!-- The remaining entries are optional, but strongly recommended, for the first Slam Stick product (defaults will be used); required if different from the defaults. -->
|
||||||
|
<!-- Note, the production device implements a proper EBML parser for this stuff, so the "length" values shown for struct-like access are retained only as a note to the implementer regarding max. payload size for the field.-->
|
||||||
|
|
||||||
|
<!-- Config structure -->
|
||||||
|
<!-- Consists of the ConfigurationList master element and children, exactly as it will appear in the output. -->
|
||||||
|
<!-- Entry 0 contains a single ValuePolynomial corresponding to the 3-axis accelerometer (channel 0) -->
|
||||||
|
<!-- Entries 1-3 contain a ValuePolynomial and optional TempCoPolynomial corresponding to accelerometer subchannels 0..2, respectively. -->
|
||||||
|
|
||||||
|
|
||||||
|
<MasterElement name="DeviceManifest" level="0" id="0x184D414E" mandatory="0" multiple="0" minver="1">Class D (4 byte + 2 length) Device Manifest master. Presence of this master tells the device a valid manifest is present.
|
||||||
|
<MasterElement name="SystemInfo" id="0x4D00" mandatory="0" multiple="0" minver="1">Class B (2 byte + 2 length) Master element for the basic system info (serial#, etc).
|
||||||
|
<UIntegerElement name="SerialNumber" id="0x4D01" mandatory="1" multiple="0" minver="1" length="4">4-byte Vendor-defined (MIDE) product serial number.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DeviceTypeUID" id="0x4D02" mandatory="1" multiple="0" minver="1" length="4">4-byte Unique hardware configuration ID used to distinguish recorder types/variants.</UIntegerElement>
|
||||||
|
<StringElement name="ProductName" id="0x4D03" mandatory="1" multiple="0" minver="1">20-byte Displayable text name for this product/variant.</StringElement>
|
||||||
|
<UIntegerElement name="HwRev" id="0x4D04" mandatory="1" multiple="0" minver="1" length="2">Hardware revision code for this DeviceTypeUID.</UIntegerElement>
|
||||||
|
<!-- FwRev and FwRevString, if applicable, are hardcoded in the FW itself and don't need a Manifest schema entry, They do, however, have a MIDE schema entry under RecorderInfo. -->
|
||||||
|
<UIntegerElement name="MinFwRev" id="0x4D05" mandatory="1" multiple="0" minver="1" length="2">Minimum FW revision code required to support this hardware.</UIntegerElement>
|
||||||
|
<UIntegerElement name="DateOfManufacture" id="0x4D06" mandatory="1" multiple="0" minver="1" length="8">8-byte date/time of manufacture (initial programming) in UTC seconds since the Epoch.</UIntegerElement>
|
||||||
|
<!--<UIntegerElement name="Vdd" id="0x4D07" mandatory="1" multiple="0" minver="1" length="3">Vdd value in mV.</UIntegerElement>-->
|
||||||
|
<!--<UIntegerElement name="Vref" id="0x4D08" mandatory="1" multiple="0" minver="1" length="3">ADC Vref value in mV.</UIntegerElement>-->
|
||||||
|
<StringElement name="PartNumber" id="0x4D09" mandatory="1" multiple="0" minver="1">Device part number string (e.g. VR002-100-XYZ).</StringElement>
|
||||||
|
<StringElement name="HwCustomStr" id="0x4D0A" mandatory="0" multiple="0" minver="1">Custom hardware identifier. Hardware is a custom version if present.</StringElement>
|
||||||
|
<StringElement name="FwCustomStr" id="0x4D0B" mandatory="0" multiple="0" minver="1">Custom firmware build. Firmware is a custom build if present. Name should match FW branch/tag name as applicable for identification purposes, but is mainly present so FW updater can generate a warning if a custom build will be replaced by a standard one.</StringElement>
|
||||||
|
<UIntegerElement name="HwAPI" id="0x4D0C" mandatory="0" multiple="0" minver="1">Hardware API level of this hardware. This is bumped in response to hardware revs with compatibility implications.</UIntegerElement>
|
||||||
|
<StringElement name="BatchIDStr" id="0x4D0D" mandatory="0" multiple="0" minver="1">Batch identification string. Text string encoding the date, variant and manufacturer of a given hardware batch. Batch is independent of HwRev as there may be multiple production batches of a given revision, from multiple fab houses.</StringElement>
|
||||||
|
</MasterElement>
|
||||||
|
<!-- Battery Info -->
|
||||||
|
<MasterElement name="Battery" id="0x4D10" mandatory="0" multiple="0" minver="1">Class B (2 byte + 2 length) Master element for battery info.
|
||||||
|
<UIntegerElement name="Capacity_mAh" id="0x4D11" mandatory="0" multiple="0" minver="1">4-byte Nominal battery capacity in mAh.</UIntegerElement>
|
||||||
|
<UIntegerElement name="Vddscale_full_thresh" id="0x4D12" mandatory="0" multiple="0" minver="1">1-byte ACMP Vdd scale value corresponding to battery full threshold (0 ~ 63).</UIntegerElement>
|
||||||
|
<UIntegerElement name="Vddscale_ok_thresh" id="0x4D13" mandatory="0" multiple="0" minver="1">1-byte ACMP Vdd scale value corresponding to battery 'ok' threshold (0 ~ 63).</UIntegerElement>
|
||||||
|
<UIntegerElement name="Vddscale_low_thresh" id="0x4D14" mandatory="0" multiple="0" minver="1">1-byte ACMP Vdd scale value corresponding to low-battery alarm threshold (0 ~ 63).</UIntegerElement>
|
||||||
|
<UIntegerElement name="Vddscale_dead_thresh" id="0x4D15" mandatory="0" multiple="0" minver="1">1-byte ACMP Vdd scale value corresponding to low-battery poweroff threshold (0 ~ 63).</UIntegerElement>
|
||||||
|
<UIntegerElement name="Vsd" id="0x4D16" mandatory="1" multiple="0" minver="1">Hardware cutoff voltage value in mV.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<!-- Information about the antialiasing filter bank, if present. -->
|
||||||
|
<MasterElement name="AAFilterInfo" id="0x4D20" mandatory="0" multiple="0" minver="1">Class B (2 byte + 2 length) Master element for the antialiasing filter info.
|
||||||
|
<UIntegerElement name="FilterResponse" id="0x4D21" mandatory="1" multiple="0" minver="1">1-byte filter type code (0=Butterworth, others currently undefined).</UIntegerElement>
|
||||||
|
<UIntegerElement name="FilterOrder" id="0x4D22" mandatory="1" multiple="0" minver="1">1-byte filter order value.</UIntegerElement>
|
||||||
|
<UIntegerElement name="ClockToCornerRatio" id="0x4D23" mandatory="1" multiple="0" minver="1">2-byte Ratio between input clock frequency and resulting filter corner frequency (clock-tunable filters only).</UIntegerElement>
|
||||||
|
<UIntegerElement name="FMin" id="0x4D24" mandatory="1" multiple="0" minver="1">4-byte Minimum allowed corner frequency in Hz.</UIntegerElement>
|
||||||
|
<UIntegerElement name="FMax" id="0x4D25" mandatory="1" multiple="0" minver="1">4-byte Maximum allowed corner frequency in Hz.</UIntegerElement>
|
||||||
|
<UIntegerElement name="Bypassable" id="0x4D26" mandatory="1" multiple="0" minver="1">1-byte Boolean specifying that filter bypass is supported (low-fidelity data acquisition allowed in the disabled state). Nonzero = true.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<!-- Specifies one arbitrary analog sensor. -->
|
||||||
|
<MasterElement name="AnalogSensorInfo" id="0x4D30" mandatory="0" multiple="1" minver="1">Class B (2 byte + 2 length) Master element for one analog sensor of one or more channels.
|
||||||
|
<UIntegerElement name="AnalogSensorEntryID" id="0x4D31" mandatory="0" multiple="0" minver="1">1-byte Locally unique ID for referencing this entry. Used as ChannelID.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AnalogSensorDeviceCode" id="0x4D32" mandatory="0" multiple="0" minver="1">1-byte Code identifying the analog sensor. 0 = ADXL001, 1 = 832M1, others=TBD.</UIntegerElement>
|
||||||
|
<StringElement name="AnalogSensorSerialNumber" id="0x4D33" mandatory="0" multiple="0" minver="1">16-byte string for the sensor manufacturer's serial #. This is a string because some vendors (including that of 832M1) love to mix in letters, hyphens and other nonnumeric elements.</StringElement>
|
||||||
|
<!-- Units? Probably handled bo 'plot'; device does not need to know. -->
|
||||||
|
<UIntegerElement name="AnalogSensorUsesAAFilter" id="0x4D34" mandatory="0" multiple="0" minver="1">1-byte Boolean specifies that this sensor goes through the AA filter. Nonzero = true.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AnalogSensorSettlingTime" id="0x4D35" mandatory="0" multiple="0" minver="1">2-byte Sensor start up + settling time in ticks (1/32768s)</UIntegerElement>
|
||||||
|
<StringElement name="AnalogSensorName" id="0x4D36" mandatory="0" multiple="0" minver="1">16-byte string for the sensor name / part#. This will probably be overridden by 'Plot'.</StringElement>
|
||||||
|
<UIntegerElement name="AnalogSensorCalIDRef" id="0x4D37" mandatory="0" multiple="0" minver="1">1-byte Reference to a SensorChannel-level CalID. This cal entry will simply determine the fixed sensor scaling and offset.</UIntegerElement>
|
||||||
|
<IntegerElement name="AnalogSensorScaleHintI" id="0x4D38" mandatory="0" multiple="0" minver="1">4-byte int32 hint expressing the nominal sensor scale and whether it is inverted. For sensors (accelerometer) where one of several nominal sensitivities can be stuffed. This value is optionally used by the device to distinguish stuffed sensitivities without having to interpret a calibration element.</IntegerElement>
|
||||||
|
<FloatElement name="AnalogSensorScaleHintF" id="0x4D39" mandatory="0" multiple="0" minver="1">4-byte float hint expressing the nominal sensor scale and whether it is inverted. For sensors (accelerometer) where one of several nominal sensitivities can be stuffed. This value is optionally used by the device to distinguish stuffed sensitivities without having to interpret a calibration element.</FloatElement>
|
||||||
|
<MasterElement name="AnalogSensorChannel" id="0x4D50" mandatory="0" multiple="1" minver="1">Class B (2 byte + 2 length) Master element for one sensor channel.
|
||||||
|
<UIntegerElement name="AnalogSensorChannelEntryID" id="0x4D51" mandatory="0" multiple="0" minver="1">1-byte Entry ID for this channel.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AnalogSensorChannelADCChannel" id="0x4D52" mandatory="0" multiple="0" minver="1">1-byte ADC channel number corresponding to this sensor channel.</UIntegerElement>
|
||||||
|
<StringElement name="AnalogSensorChannelAxisName" id="0x4D53" mandatory="0" multiple="0" minver="1">16-byte Axis name associated with this sensor channel.</StringElement>
|
||||||
|
<UIntegerElement name="AnalogSensorChannelCalIDRef" id="0x4D54" mandatory="0" multiple="0" minver="1">1-byte Reference to a SensorSubChannel-level CalID. This will store actual sensor calibration modifying the basic scale/offset parameters.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AnalogSensorBWLowerCutoff" id="0x4D55" mandatory="0" multiple="0" minver="1">4-byte Lower cutoff frequency in Hz.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AnalogSensorBWUpperCutoff" id="0x4D56" mandatory="0" multiple="0" minver="1">4-byte Upper cutoff frequency in Hz.</UIntegerElement>
|
||||||
|
<UIntegerElement name="AnalogSensorChannelCTF" id="0x4D57" mandatory="0" multiple="0" minver="1" default="1">CTF filter.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
</MasterElement>
|
||||||
|
<!-- Codes for specific digital sensors and storage devices. We can't make this all-singing-all-dancing generic, but can at least tell the FW if specific sensors at known e.g. bus locations are stuffed or not. -->
|
||||||
|
<!-- Media elements (SD, eventually wireless/etc.?) -->
|
||||||
|
<MasterElement name="DigitalStorageSD" id="0x4D80" mandatory="0" multiple="0" minver="1">Indicates the presence of a uSD card on the SPI bus.</MasterElement>
|
||||||
|
|
||||||
|
<!-- Digital inertial sensors (accelerometer, gyro, IMU, etc.) -->
|
||||||
|
<MasterElement name="DigitalSensorADXL362" id="0x4D90" mandatory="0" multiple="0" minver="1">Indicates the presence of an ADXL362 accelerometer on the SPI bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorADXL345" id="0x4D91" mandatory="0" multiple="0" minver="1">Indicates the presence of an ADXL345 accelerometer on the SPI bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorADXL355" id="0x4DD4" mandatory="0" multiple="0" minver="1">Indicates the presence of an ADXL355 accelerometer on the SPI bus.
|
||||||
|
<UIntegerElement name="SensorConfig" id="0x4E00" mandatory="0" multiple="0" minver="1">Configuration data for digital sensors. Value varies by hardware.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorADXL357" id="0x4DD5" mandatory="0" multiple="0" minver="1">Indicates the presence of an ADXL357 accelerometer on the SPI bus.
|
||||||
|
<UIntegerElement name="SensorConfig" id="0x4E00" mandatory="0" multiple="0" minver="1">Configuration data for digital sensors. Value varies by hardware.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorADXL375" id="0x4D92" mandatory="0" multiple="0" minver="1">Indicates the presence of an ADXL375 accelerometer on the SPI bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorBNO055" id="0x4D93" mandatory="0" multiple="0" minver="1">Indicates the presence of a BNO055 IMU on the I2C0 bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorBHI160" id="0x4D94" mandatory="0" multiple="0" minver="1">Indicates the presence of a Bosch BHI160 IMU on the I2C0 bus.
|
||||||
|
<UIntegerElement name="SensorConfig" id="0x4E00" mandatory="0" multiple="0" minver="1">Configuration data for digital sensors. Value varies by hardware.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorBMG250" id="0x4D95" mandatory="0" multiple="0" minver="1">Indicates the presence of a BMG250 Gyroscope</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorBMG250Int" id="0x4D96" mandatory="0" multiple="0" minver="1">Indicates the presence of a BMG250 Gyroscope, with an interrupt line</MasterElement>
|
||||||
|
|
||||||
|
<!-- Digital pressure/other sensors -->
|
||||||
|
<MasterElement name="DigitalSensorMPL3115A2" id="0x4DA0" mandatory="0" multiple="0" minver="1">Indicates the presence of an MPL3115A2 P/T sensor on the I2C0 bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorMS8607" id="0x4DA1" mandatory="0" multiple="0" minver="1">Indicates the presence of an MS8607 P/T/H sensor on the I2C1 bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorMS5637" id="0x4DA2" mandatory="0" multiple="0" minver="1">Indicates the presence of an MS5637 pressure sensor on the I2C1 bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorHTU21D" id="0x4DA3" mandatory="0" multiple="0" minver="1">Indicates the presence of an HTU21D humidity sensor on the I2C1 bus.</MasterElement>
|
||||||
|
|
||||||
|
<!-- Timing/Sync inputs -->
|
||||||
|
<MasterElement name="DigitalSensorIR" id="0x4DB0" mandatory="0" multiple="0" minver="1">Indicates the presence of a digital IR sensor.</MasterElement>
|
||||||
|
|
||||||
|
<!-- GPS/Sync inputs -->
|
||||||
|
<MasterElement name="DigitalSensorGPS_UART" id="0x4DC0" mandatory="0" multiple="0" minver="1">Indicates the presence of a GPS with full data (location) broken out on UART.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorGPS_CAMM8" id="0x4DC1" mandatory="0" multiple="0" minver="1">Indicates the presence of a GPS with full data (location) broken out on UART.
|
||||||
|
<UIntegerElement name="SensorConfig" id="0x4E00" mandatory="0" multiple="0" minver="1">Configuration data for digital sensors. Value varies by hardware.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
<!-- Battery charger ICs -->
|
||||||
|
<MasterElement name="ChargerMCP73837" id="0x4D60" mandatory="0" multiple="0" minver="1">Indicates the presence of a MCP73837 battery charger.</MasterElement>
|
||||||
|
<MasterElement name="ChargerMAX14747" id="0x4D61" mandatory="0" multiple="0" minver="1">Indicates the presence of a MAX14747 battery charger.</MasterElement>
|
||||||
|
<MasterElement name="ChargerNONE" id="0x4D6F" mandatory="0" multiple="0" minver="1">Indicates the presence of no battery charger or just a single cell battery.</MasterElement>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Communication -->
|
||||||
|
<MasterElement name="CommunicationWiFi_ESP32" id="0x4D70" mandatory="0" multiple="0" minver="1">Indicates the presence of an ESP32 Wi-Fi module.</MasterElement>
|
||||||
|
|
||||||
|
<!-- Misc. -->
|
||||||
|
<MasterElement name="DigitalSensorReset" id="0x4DD0" mandatory="0" multiple="0" minver="1">Indicates the presence of a hardware pushbutton reset controller.</MasterElement>
|
||||||
|
<MasterElement name="DigitalPowerMAX77801" id="0x4DD1" mandatory="0" multiple="0" minver="1">Indicates the presence of a MAX77801 power management IC on the I2C1 bus.</MasterElement>
|
||||||
|
<MasterElement name="DigitalLedThree" id="0x4DD2" mandatory="0" multiple="0" minver="1">Indicates that the membrane has a 3rd LED driven by the MCU.</MasterElement>
|
||||||
|
<MasterElement name="DigitalSensorSI1133" id="0x4DD3" mandatory="0" multiple="0" minver="1">Indicates the presence of a SI1133 light sensor on the I2C1 bus.</MasterElement>
|
||||||
|
|
||||||
|
<!-- Analog Out -->
|
||||||
|
<MasterElement name="PeripheralHeater" id="0x4F00" mandatory="0" multiple="0" minver="1">Indicates the presence of an analog heater device.
|
||||||
|
<UIntegerElement name="PeripheralConfig" id="0x4F01" mandatory="0" multiple="0" minver="1">Configuration data for non-sensor peripherals. For the heater, it is the output power in milliwatts.</UIntegerElement>
|
||||||
|
</MasterElement>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</MasterElement>
|
||||||
|
</Schema>
|
||||||
269
lambda/cctv-people-rekognition/ebmlite/threaded_file.py
Normal file
269
lambda/cctv-people-rekognition/ebmlite/threaded_file.py
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
'''
|
||||||
|
A special-case, drop-in 'replacement' for a standard read-only file stream
|
||||||
|
that supports simultaneous access by multiple threads without (explicit)
|
||||||
|
blocking. Each thread actually gets its own stream, so it can perform its
|
||||||
|
own seeks without affecting other threads that may be reading the file. This
|
||||||
|
functionality is transparent.
|
||||||
|
|
||||||
|
@author: dstokes
|
||||||
|
'''
|
||||||
|
__author__ = "David Randall Stokes, Connor Flanigan"
|
||||||
|
__copyright__ = "Copyright 2021, Mide Technology Corporation"
|
||||||
|
__credits__ = "David Randall Stokes, Connor Flanigan, Becker Awqatty, Derek Witt"
|
||||||
|
|
||||||
|
__all__ = ['ThreadAwareFile']
|
||||||
|
|
||||||
|
import io
|
||||||
|
import platform
|
||||||
|
from threading import currentThread, Event
|
||||||
|
|
||||||
|
class ThreadAwareFile(io.FileIO):
|
||||||
|
""" A 'replacement' for a standard read-only file stream that supports
|
||||||
|
simultaneous access by multiple threads without (explicit) blocking.
|
||||||
|
Each thread actually gets its own stream, so it can perform its own
|
||||||
|
seeks without affecting other threads that may be reading the file.
|
||||||
|
This functionality is transparent.
|
||||||
|
|
||||||
|
ThreadAwareFile implements the standard `file` methods and has
|
||||||
|
the standard attributes and properties. Most of these affect only
|
||||||
|
the current thread.
|
||||||
|
|
||||||
|
@var timeout: A value (in seconds) for blocking operations to wait.
|
||||||
|
Very few operations block; specifically, only those that do
|
||||||
|
(or depend upon) internal housekeeping. Timeout should only occur
|
||||||
|
in certain extreme conditions (e.g. filesystem-related file
|
||||||
|
access issues).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
""" ThreadAwareFile(name[, mode[, buffering]]) -> file object
|
||||||
|
|
||||||
|
Open a read-only file that may already be open in other threads.
|
||||||
|
Takes the standard `file` arguments, except `mode` can only be
|
||||||
|
one of the "read" modes (``r``, ``rb``, ``rU``, etc.).
|
||||||
|
"""
|
||||||
|
# Ensure the file mode, if specified, is "read."
|
||||||
|
mode = args[1] if len(args) > 1 else 'r'
|
||||||
|
if isinstance(mode, (str, bytes, bytearray)):
|
||||||
|
if 'a' in mode or 'w' in mode or '+' in mode:
|
||||||
|
raise IOError("%s is read-only" % self.__class__.__name__)
|
||||||
|
|
||||||
|
# Undocumented keyword argument `_new` is used by `makeThreadAware()`
|
||||||
|
# to prevent a new file for the current thread from being created.
|
||||||
|
newFile = kwargs.pop('_new', True)
|
||||||
|
|
||||||
|
# Blocking timeout. Not a `file` keyword argument; remove.
|
||||||
|
self.timeout = kwargs.pop('timeout', 60.0)
|
||||||
|
|
||||||
|
self.initArgs = args
|
||||||
|
self.initKwargs = kwargs
|
||||||
|
|
||||||
|
self._ready = Event() # NOT a lock; some things block, others wait
|
||||||
|
self._ready.set()
|
||||||
|
|
||||||
|
self.threads = {}
|
||||||
|
|
||||||
|
if newFile is True:
|
||||||
|
# Getting the stream for the thread will open the file.
|
||||||
|
self.getThreadStream()
|
||||||
|
|
||||||
|
# For repr() on files closed by a thread.
|
||||||
|
self._mode = mode
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
# Format the object's ID appropriately for the architecture (32b/64b)
|
||||||
|
if '32' in platform.architecture()[0]:
|
||||||
|
fmt = "<%s %s %r, mode %r at 0x%08X>"
|
||||||
|
else:
|
||||||
|
fmt = "<%s %s %r, mode %r at 0x%016X>"
|
||||||
|
|
||||||
|
return fmt % ("closed" if self.closed else "open",
|
||||||
|
self.__class__.__name__,
|
||||||
|
self.initArgs[0],
|
||||||
|
self._mode,
|
||||||
|
id(self))
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def makeThreadAware(cls, fileStream):
|
||||||
|
""" Create a new `ThreadAwareFile` from an already-open file. If the
|
||||||
|
object is a `ThreadAwareFile`, it is returned verbatim.
|
||||||
|
"""
|
||||||
|
if isinstance(fileStream, cls):
|
||||||
|
return fileStream
|
||||||
|
elif not isinstance(fileStream, io.IOBase):
|
||||||
|
raise TypeError("Not a file: %r" % fileStream)
|
||||||
|
|
||||||
|
f = cls(fileStream.name, fileStream.mode, _new=False)
|
||||||
|
f.threads[currentThread().ident] = fileStream
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
def getThreadStream(self):
|
||||||
|
""" Get (or create) the file stream for the current thread.
|
||||||
|
"""
|
||||||
|
self._ready.wait(self.timeout)
|
||||||
|
|
||||||
|
ident = currentThread().ident
|
||||||
|
if ident not in self.threads:
|
||||||
|
# First access from this thread. Open the file.
|
||||||
|
fp = io.FileIO(*self.initArgs, **self.initKwargs)
|
||||||
|
self.threads[ident] = fp
|
||||||
|
return fp
|
||||||
|
return self.threads[ident]
|
||||||
|
|
||||||
|
|
||||||
|
def closeAll(self):
|
||||||
|
""" Close all open streams.
|
||||||
|
|
||||||
|
Warning: May not be thread-safe in some situations!
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._ready.wait(self.timeout)
|
||||||
|
self._ready.clear()
|
||||||
|
for v in list(self.threads.values()):
|
||||||
|
v.close()
|
||||||
|
finally:
|
||||||
|
self._ready.set()
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
""" Delete all closed streams.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._ready.wait(self.timeout)
|
||||||
|
self._ready.clear()
|
||||||
|
|
||||||
|
for i in self.threads.keys():
|
||||||
|
if self.threads[i].closed:
|
||||||
|
del self.threads[i]
|
||||||
|
finally:
|
||||||
|
self._ready.set()
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
""" Is the file not open? Note: A thread that never accessed the file
|
||||||
|
will get `True`.
|
||||||
|
"""
|
||||||
|
ident = currentThread().ident
|
||||||
|
if ident in self.threads:
|
||||||
|
return self.threads[ident].closed
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def close(self, *args, **kwargs):
|
||||||
|
""" Close the file for the current thread. The file will remain
|
||||||
|
open for other threads.
|
||||||
|
"""
|
||||||
|
result = self.getThreadStream().close(*args, **kwargs)
|
||||||
|
self.cleanup()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# Standard file methods, overridden
|
||||||
|
|
||||||
|
def __format__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__format__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __hash__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__hash__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __iter__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__iter__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __reduce__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__reduce__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __reduce_ex__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__reduce_ex__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __sizeof__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__sizeof__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __str__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__str__(*args, **kwargs)
|
||||||
|
|
||||||
|
def fileno(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().fileno(*args, **kwargs)
|
||||||
|
|
||||||
|
def flush(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().flush(*args, **kwargs)
|
||||||
|
|
||||||
|
def isatty(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().isatty(*args, **kwargs)
|
||||||
|
|
||||||
|
def next(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().next(*args, **kwargs)
|
||||||
|
|
||||||
|
def read(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().read(*args, **kwargs)
|
||||||
|
|
||||||
|
def readinto(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().readinto(*args, **kwargs)
|
||||||
|
|
||||||
|
def readline(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().readline(*args, **kwargs)
|
||||||
|
|
||||||
|
def readlines(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().readlines(*args, **kwargs)
|
||||||
|
|
||||||
|
def seek(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().seek(*args, **kwargs)
|
||||||
|
|
||||||
|
def tell(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().tell(*args, **kwargs)
|
||||||
|
|
||||||
|
def truncate(self, *args, **kwargs):
|
||||||
|
raise IOError("Can't truncate(); %s is read-only" %
|
||||||
|
self.__class__.__name__)
|
||||||
|
|
||||||
|
def write(self, *args, **kwargs):
|
||||||
|
raise IOError("Can't write(); %s is read-only" %
|
||||||
|
self.__class__.__name__)
|
||||||
|
|
||||||
|
def writelines(self, *args, **kwargs):
|
||||||
|
raise IOError("Can't writelines(); %s is read-only" %
|
||||||
|
self.__class__.__name__)
|
||||||
|
|
||||||
|
def xreadlines(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().xreadlines(*args, **kwargs)
|
||||||
|
|
||||||
|
def __enter__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__enter__(*args, **kwargs)
|
||||||
|
|
||||||
|
def __exit__(self, *args, **kwargs):
|
||||||
|
return self.getThreadStream().__exit__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# Standard file attributes, as properties for transparency with 'real'
|
||||||
|
# file objects. Most are read-only.
|
||||||
|
|
||||||
|
@property
|
||||||
|
def encoding(self):
|
||||||
|
return self.getThreadStream().encoding
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors(self):
|
||||||
|
return self.getThreadStream().errors
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode(self):
|
||||||
|
return self.getThreadStream().mode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.getThreadStream().name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def newlines(self):
|
||||||
|
return self.getThreadStream().newlines
|
||||||
|
|
||||||
|
@property
|
||||||
|
def softspace(self):
|
||||||
|
return self.getThreadStream().softspace
|
||||||
|
|
||||||
|
@softspace.setter
|
||||||
|
def softspace(self, val):
|
||||||
|
self.getThreadStream().softspace = val
|
||||||
76
lambda/cctv-people-rekognition/ebmlite/tools/ebml2xml.py
Normal file
76
lambda/cctv-people-rekognition/ebmlite/tools/ebml2xml.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import argparse
|
||||||
|
from xml.dom.minidom import parseString
|
||||||
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
|
from ebmlite.tools import utils
|
||||||
|
import ebmlite.util
|
||||||
|
import ebmlite.xml_codecs
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Build help text listing the binary codecs, and get the default one.
|
||||||
|
codecs = list(ebmlite.xml_codecs.BINARY_CODECS)
|
||||||
|
default_codec = codecs[0]
|
||||||
|
codec_desc = ""
|
||||||
|
for name, codec in ebmlite.xml_codecs.BINARY_CODECS.items():
|
||||||
|
name = '"{}"'.format(name)
|
||||||
|
if codec.NAME == default_codec:
|
||||||
|
name += ' (default)'.format(name)
|
||||||
|
codec_desc += '{}: {}\n'.format(name, " ".join(codec.__doc__.split()))
|
||||||
|
|
||||||
|
argparser = argparse.ArgumentParser(
|
||||||
|
description="A tool for converting ebml to xml."
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'input', metavar="FILE.ebml", help="The source EBML file.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'schema',
|
||||||
|
metavar="SCHEMA.xml",
|
||||||
|
help=(
|
||||||
|
"The name of the schema file. Only the name itself is required if"
|
||||||
|
" the schema file is in the standard schema directory."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-o', '--output', metavar="FILE.xml", help="The output file.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-c', '--clobber', action="store_true",
|
||||||
|
help="Clobber (overwrite) existing files.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-s', '--single', action="store_true", help="Generate XML as a single line with no newlines or indents",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-m', '--max',
|
||||||
|
action="store_true",
|
||||||
|
help="Generate XML with maximum description, including offset, size, type, and id info",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-e', '--encoding',
|
||||||
|
choices=codecs,
|
||||||
|
default=default_codec,
|
||||||
|
help="The method of encoding binary data as text.\n" + codec_desc
|
||||||
|
)
|
||||||
|
|
||||||
|
args = argparser.parse_args()
|
||||||
|
|
||||||
|
codecargs = {'cols': None} if args.single else {}
|
||||||
|
codec = ebmlite.xml_codecs.BINARY_CODECS[args.encoding.strip().lower()](**codecargs)
|
||||||
|
|
||||||
|
with utils.load_files(args, binary_output=args.single) as (schema, out):
|
||||||
|
doc = schema.load(args.input, headers=True)
|
||||||
|
if args.max:
|
||||||
|
root = ebmlite.util.toXml(doc, offsets=True, sizes=True, types=True, ids=True, binary_codec=codec)
|
||||||
|
else:
|
||||||
|
root = ebmlite.util.toXml(doc, offsets=False, sizes=False, types=False, ids=False, binary_codec=codec)
|
||||||
|
s = ET.tostring(root, encoding="utf-8")
|
||||||
|
if args.single:
|
||||||
|
out.write(s)
|
||||||
|
else:
|
||||||
|
parseString(s).writexml(out, addindent='\t', newl='\n', encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
A tool for listing all EBML schemata in SCHEMA_PATH, including paths in the
|
||||||
|
EBMLITE_SCHEMA_PATH (if present), and (optionally) any additional paths
|
||||||
|
specified by the user. Additional paths may include module names enclosed in
|
||||||
|
braces (e.g., "{idelib}").
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import ebmlite.util
|
||||||
|
import ebmlite.core
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argparser = argparse.ArgumentParser(description=__doc__.strip())
|
||||||
|
|
||||||
|
argparser.add_argument(
|
||||||
|
'-o', '--output', metavar="FILE.txt", help="An optional output file",
|
||||||
|
default=sys.stdout
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-r', '--relative', action="store_true",
|
||||||
|
help="Show schema filenames with package-relative path references",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'paths', nargs='*',
|
||||||
|
help="Additional paths to search for schemata; will be searched before paths in SCHEMA_PATH"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = argparser.parse_args()
|
||||||
|
ebmlite.util.printSchemata(paths=args.paths, out=args.output, absolute=not args.relative)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
36
lambda/cctv-people-rekognition/ebmlite/tools/utils.py
Normal file
36
lambda/cctv-people-rekognition/ebmlite/tools/utils.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import contextlib
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from ebmlite import core
|
||||||
|
|
||||||
|
|
||||||
|
def errPrint(msg):
|
||||||
|
sys.stderr.write("%s\n" % msg)
|
||||||
|
sys.stderr.flush()
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def load_files(args, binary_output=False):
|
||||||
|
if not os.path.exists(args.input):
|
||||||
|
sys.stderr.write("Input file does not exist: %s\n" % args.input)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
schema_file = args.schema
|
||||||
|
if os.path.splitext(schema_file.strip())[1] == '':
|
||||||
|
schema_file += '.xml'
|
||||||
|
schema = core.loadSchema(schema_file)
|
||||||
|
except IOError as err:
|
||||||
|
errPrint("Error loading schema: %s\n" % err)
|
||||||
|
|
||||||
|
if not args.output:
|
||||||
|
yield (schema, sys.stdout)
|
||||||
|
return
|
||||||
|
|
||||||
|
output = os.path.realpath(os.path.expanduser(args.output))
|
||||||
|
if os.path.exists(output) and not args.clobber:
|
||||||
|
errPrint("Error: Output file already exists: %s" % args.output)
|
||||||
|
with open(output, ('wb' if binary_output else 'w')) as out:
|
||||||
|
yield (schema, out)
|
||||||
57
lambda/cctv-people-rekognition/ebmlite/tools/view_ebml.py
Normal file
57
lambda/cctv-people-rekognition/ebmlite/tools/view_ebml.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
from ebmlite.tools import utils
|
||||||
|
import ebmlite.util
|
||||||
|
import ebmlite.xml_codecs
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Build help text listing the binary codecs, and get the default one.
|
||||||
|
codecs = list(ebmlite.xml_codecs.BINARY_CODECS)
|
||||||
|
default_codec = "ignore"
|
||||||
|
codec_desc = ""
|
||||||
|
for name, codec in ebmlite.xml_codecs.BINARY_CODECS.items():
|
||||||
|
name = '"{}"'.format(name)
|
||||||
|
if codec.NAME == default_codec:
|
||||||
|
name += ' (default)'.format(name)
|
||||||
|
codec_desc += '{}: {}\n'.format(name, " ".join(codec.__doc__.split()))
|
||||||
|
|
||||||
|
argparser = argparse.ArgumentParser(
|
||||||
|
description="A tool for reading ebml file content."
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'input', metavar="FILE.ebml", help="The source XML file.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'schema',
|
||||||
|
metavar="SCHEMA.xml",
|
||||||
|
help=(
|
||||||
|
"The name of the schema file. Only the name itself is required if"
|
||||||
|
" the schema file is in the standard schema directory."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-o', '--output', metavar="FILE.xml", help="The output file.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-c', '--clobber', action="store_true",
|
||||||
|
help="Clobber (overwrite) existing files.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-e', '--encoding',
|
||||||
|
choices=codecs,
|
||||||
|
default=default_codec,
|
||||||
|
help="The method of encoding binary data as text.\n" + codec_desc
|
||||||
|
)
|
||||||
|
|
||||||
|
args = argparser.parse_args()
|
||||||
|
|
||||||
|
codec = ebmlite.xml_codecs.BINARY_CODECS[args.encoding.strip().lower()]()
|
||||||
|
|
||||||
|
with utils.load_files(args, binary_output=False) as (schema, out):
|
||||||
|
doc = schema.load(args.input, headers=True)
|
||||||
|
ebmlite.util.pprint(doc, out=out, binary_codec=codec)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
36
lambda/cctv-people-rekognition/ebmlite/tools/xml2ebml.py
Normal file
36
lambda/cctv-people-rekognition/ebmlite/tools/xml2ebml.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
from ebmlite.tools import utils
|
||||||
|
import ebmlite.util
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argparser = argparse.ArgumentParser(
|
||||||
|
description="A tool for converting xml to ebml."
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'input', metavar="FILE.xml", help="The source XML file.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'schema',
|
||||||
|
metavar="SCHEMA.xml",
|
||||||
|
help=(
|
||||||
|
"The name of the schema file. Only the name itself is required if"
|
||||||
|
" the schema file is in the standard schema directory."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-o', '--output', metavar="FILE.ebml", help="The output file.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
'-c', '--clobber', action="store_true",
|
||||||
|
help="Clobber (overwrite) existing files.",
|
||||||
|
)
|
||||||
|
args = argparser.parse_args()
|
||||||
|
|
||||||
|
with utils.load_files(args, binary_output=True) as (schema, out):
|
||||||
|
ebmlite.util.xml2ebml(args.input, out, schema) # , sizeLength=4, headers=True, unknown=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
475
lambda/cctv-people-rekognition/ebmlite/util.py
Normal file
475
lambda/cctv-people-rekognition/ebmlite/util.py
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
"""
|
||||||
|
Some utilities for manipulating EBML documents: translate to/from XML, etc.
|
||||||
|
This module may be imported or used as a command-line utility.
|
||||||
|
|
||||||
|
Created on Aug 11, 2017
|
||||||
|
|
||||||
|
@todo: Clean up and standardize usage of the term 'size' versus 'length.'
|
||||||
|
@todo: Modify (or create an alternate version of) `toXml()` that writes
|
||||||
|
directly to a file, allowing the conversion of huge EBML files.
|
||||||
|
@todo: Add other options to command-line utility for the other arguments of
|
||||||
|
`toXml()` and `xml2ebml()`.
|
||||||
|
"""
|
||||||
|
__author__ = "David Randall Stokes, Connor Flanigan"
|
||||||
|
__copyright__ = "Copyright 2021, Mide Technology Corporation"
|
||||||
|
__credits__ = "David Randall Stokes, Connor Flanigan, Becker Awqatty, Derek Witt"
|
||||||
|
|
||||||
|
__all__ = ['createID', 'validateID', 'toXml', 'xml2ebml', 'loadXml', 'pprint',
|
||||||
|
'printSchemata']
|
||||||
|
|
||||||
|
import ast
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
from io import StringIO
|
||||||
|
import pathlib
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
|
from . import core, encoding, decoding
|
||||||
|
from . import xml_codecs
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def createID(schema, idClass, exclude=(), minId=0x81, maxId=0x1FFFFFFE, count=1):
|
||||||
|
""" Generate unique EBML IDs. Primarily intended for use 'offline' by
|
||||||
|
humans creating EBML schemata.
|
||||||
|
|
||||||
|
@param schema: The `Schema` in which the new IDs must coexist.
|
||||||
|
@param idClass: The EBML class of ID, one of (case-insensitive):
|
||||||
|
* `'a'`: Class A (1 octet, base 0x8X)
|
||||||
|
* `'b'`: Class B (2 octets, base 0x4000)
|
||||||
|
* `'c'`: Class C (3 octets, base 0x200000)
|
||||||
|
* `'d'`: Class D (4 octets, base 0x10000000)
|
||||||
|
@param exclude: A list of additional IDs to avoid.
|
||||||
|
@param minId: The minimum ID value, within the ID class' range.
|
||||||
|
@param maxId: The maximum ID value, within the ID class' range.
|
||||||
|
@param count: The maximum number of IDs to generate. The result may be
|
||||||
|
fewer than specified if too few meet the given criteria.
|
||||||
|
@return: A list of EBML IDs that match the given criteria.
|
||||||
|
"""
|
||||||
|
ranges = dict(A=(0x81, 0xFE),
|
||||||
|
B=(0x407F, 0x7FFE),
|
||||||
|
C=(0x203FFF, 0x3FFFFE),
|
||||||
|
D=(0x101FFFFF, 0x1FFFFFFE))
|
||||||
|
idc = idClass.upper()
|
||||||
|
if idc not in ranges:
|
||||||
|
raise KeyError('Invalid ID class %r: must be one of %r' %
|
||||||
|
(idClass, list(ranges)))
|
||||||
|
|
||||||
|
# Keep range within the one specified and the one imposed by the ID class
|
||||||
|
idrange = (max(ranges[idc][0], minId),
|
||||||
|
min(ranges[idc][1], maxId))
|
||||||
|
|
||||||
|
exclude = set(exclude).union(schema.elements.keys())
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for i in (x for x in range(*idrange) if x not in exclude):
|
||||||
|
if len(result) == count:
|
||||||
|
break
|
||||||
|
result.append(i)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def validateID(elementId):
|
||||||
|
""" Verify that a number is a valid EBML element ID. A `ValueError`
|
||||||
|
will be raised if the element ID is invalid.
|
||||||
|
|
||||||
|
Valid ranges for the four classes of EBML ID are:
|
||||||
|
* A: 0x81 to 0xFE
|
||||||
|
* B: 0x407F to 0x7FFE
|
||||||
|
* C: 0x203FFF to 0x3FFFFE
|
||||||
|
* D: 0x101FFFFF to 0x1FFFFFFE
|
||||||
|
|
||||||
|
@param elementId: The element ID to validate
|
||||||
|
@raises: `ValueError`, although certain edge cases may raise
|
||||||
|
another type.
|
||||||
|
"""
|
||||||
|
ranges = ((0x81, 0xFE), (0x407F, 0x7FFE), (0x203FFF, 0x3FFFFE), (0x101FFFFF, 0x1FFFFFFE))
|
||||||
|
|
||||||
|
msg = "Invalid element ID" # Default error message
|
||||||
|
|
||||||
|
# Basic check: is the ID within the bounds of the total ID range?
|
||||||
|
if not 0x81 <= elementId <= 0x1FFFFFFE:
|
||||||
|
raise ValueError("Element ID out of range", elementId)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# See if the first byte properly encodes the length of the ID.
|
||||||
|
s = struct.pack(">I", elementId).lstrip(b'\x00')
|
||||||
|
length, _ = decoding.decodeIDLength(s[0])
|
||||||
|
valid = len(s) == length # Should always be True if decoding worked
|
||||||
|
if valid:
|
||||||
|
minId, maxId = ranges[length-1]
|
||||||
|
if not minId <= elementId <= maxId:
|
||||||
|
msg = "ID out of range for class %s %s" % (" ABCD"[length], ranges[length-1])
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
# Note: Change this if decoding changes the exceptions it raises
|
||||||
|
except OSError as err:
|
||||||
|
valid = False
|
||||||
|
msg = err.args[0] if err.args else msg
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
raise ValueError(msg, elementId)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def toXml(el, parent=None, offsets=True, sizes=True, types=True, ids=True,
|
||||||
|
binary_codec='base64', void_codec='ignore'):
|
||||||
|
""" Convert an EBML Document to XML. Binary elements will contain
|
||||||
|
base64-encoded data in their body. Other non-master elements will
|
||||||
|
contain their value in a ``value`` attribute.
|
||||||
|
|
||||||
|
@param el: An instance of an EBML Element or Document subclass.
|
||||||
|
@keyword parent: The resulting XML element's parent element, if any.
|
||||||
|
@keyword offsets: If `True`, create a ``offset`` attributes for each
|
||||||
|
generated XML element, containing the corresponding EBML element's
|
||||||
|
offset.
|
||||||
|
@keyword sizes: If `True`, create ``size`` attributes containing the
|
||||||
|
corresponding EBML element's size.
|
||||||
|
@keyword types: If `True`, create ``type`` attributes containing the
|
||||||
|
name of the corresponding EBML element type.
|
||||||
|
@keyword ids: If `True`, create ``id`` attributes containing the
|
||||||
|
corresponding EBML element's EBML ID.
|
||||||
|
@keyword binary_codec: The name of an XML codec class from
|
||||||
|
`ebmlite.xml_codecs`, or an instance of a codec, for rendering
|
||||||
|
binary elements as text.
|
||||||
|
@keyword void_codec: The name of an XML codec class from
|
||||||
|
`ebmlite.xml_codecs`, or an instance of a codec, for rendering
|
||||||
|
the contents of Void elements as text.
|
||||||
|
@return The root XML element of the file.
|
||||||
|
"""
|
||||||
|
if isinstance(binary_codec, str):
|
||||||
|
binary_codec = xml_codecs.BINARY_CODECS[binary_codec]()
|
||||||
|
if isinstance(void_codec, str):
|
||||||
|
void_codec = xml_codecs.BINARY_CODECS[void_codec]()
|
||||||
|
|
||||||
|
if isinstance(el, core.Document):
|
||||||
|
elname = el.__class__.__name__
|
||||||
|
else:
|
||||||
|
elname = el.name
|
||||||
|
|
||||||
|
if parent is None:
|
||||||
|
xmlEl = ET.Element(elname)
|
||||||
|
else:
|
||||||
|
xmlEl = ET.SubElement(parent, elname)
|
||||||
|
if isinstance(el, core.Document):
|
||||||
|
xmlEl.set('source', el.filename)
|
||||||
|
xmlEl.set('schemaName', el.schema.name)
|
||||||
|
xmlEl.set('schemaFile', el.schema.filename)
|
||||||
|
else:
|
||||||
|
if ids and isinstance(el.id, int):
|
||||||
|
xmlEl.set('id', "0x%X" % el.id)
|
||||||
|
if types:
|
||||||
|
xmlEl.set('type', el.dtype.__name__)
|
||||||
|
|
||||||
|
if offsets:
|
||||||
|
xmlEl.set('offset', str(el.offset))
|
||||||
|
if sizes:
|
||||||
|
xmlEl.set('size', str(el.size))
|
||||||
|
|
||||||
|
if isinstance(el, core.MasterElement):
|
||||||
|
for chEl in el:
|
||||||
|
toXml(chEl, xmlEl, offsets, sizes, types, ids, binary_codec, void_codec)
|
||||||
|
elif isinstance(el, core.VoidElement):
|
||||||
|
xmlEl.set('size', str(el.size))
|
||||||
|
if void_codec.NAME != 'ignore':
|
||||||
|
xmlEl.set('encoding', void_codec.NAME)
|
||||||
|
xmlEl.text = void_codec.encode(el.value)
|
||||||
|
elif isinstance(el, core.BinaryElement):
|
||||||
|
xmlEl.set('encoding', binary_codec.NAME)
|
||||||
|
xmlEl.text = binary_codec.encode(el.value, offset=el.offset)
|
||||||
|
elif not isinstance(el, core.VoidElement):
|
||||||
|
xmlEl.set('value', str(el.value).encode('ascii', 'xmlcharrefreplace').decode())
|
||||||
|
|
||||||
|
return xmlEl
|
||||||
|
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
#
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
def xmlElement2ebml(xmlEl, ebmlFile, schema, sizeLength=None, unknown=True):
|
||||||
|
""" Convert an XML element to EBML, recursing if necessary. For converting
|
||||||
|
an entire XML document, use `xml2ebml()`.
|
||||||
|
|
||||||
|
@param xmlEl: The XML element. Its tag must match an element defined
|
||||||
|
in the `schema`.
|
||||||
|
@param ebmlFile: An open file-like stream, to which the EBML data will
|
||||||
|
be written.
|
||||||
|
@param schema: An `ebmlite.core.Schema` instance to use when
|
||||||
|
writing the EBML document.
|
||||||
|
@keyword sizeLength:
|
||||||
|
@param unknown: If `True`, unknown element names will be allowed,
|
||||||
|
provided their XML elements include an ``id`` attribute with the
|
||||||
|
EBML ID (in hexadecimal).
|
||||||
|
@return The length of the encoded element, including header and children.
|
||||||
|
@raise NameError: raised if an xml element is not present in the schema and unknown is False, OR if the xml
|
||||||
|
element does not have an ID.
|
||||||
|
"""
|
||||||
|
if not isinstance(xmlEl.tag, (str, bytes, bytearray)):
|
||||||
|
# (Probably) a comment; disregard.
|
||||||
|
return 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
cls = schema[xmlEl.tag]
|
||||||
|
encId = encoding.encodeId(cls.id)
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
# Element name not in schema. Go ahead if allowed (`unknown` is `True`)
|
||||||
|
# and the XML element specifies an ID,
|
||||||
|
if not unknown:
|
||||||
|
raise NameError("Unrecognized EBML element name: %s" % xmlEl.tag)
|
||||||
|
|
||||||
|
eid = xmlEl.get('id', None)
|
||||||
|
if eid is None:
|
||||||
|
raise NameError("Unrecognized EBML element name with no 'id' "
|
||||||
|
"attribute in XML: %s" % xmlEl.tag)
|
||||||
|
cls = core.UnknownElement
|
||||||
|
encId = encoding.encodeId(int(eid, 16))
|
||||||
|
cls.id = int(eid, 16)
|
||||||
|
|
||||||
|
codec = xmlEl.get('encoding', 'base64')
|
||||||
|
|
||||||
|
if sizeLength is None:
|
||||||
|
sl = xmlEl.get('sizeLength', None)
|
||||||
|
if sl is None:
|
||||||
|
s = xmlEl.get('size', None)
|
||||||
|
if s is not None:
|
||||||
|
sl = encoding.getLength(int(s))
|
||||||
|
else:
|
||||||
|
sl = 4
|
||||||
|
else:
|
||||||
|
sl = int(sl)
|
||||||
|
else:
|
||||||
|
sl = xmlEl.get('sizeLength', sizeLength)
|
||||||
|
|
||||||
|
if issubclass(cls, core.MasterElement):
|
||||||
|
ebmlFile.write(encId)
|
||||||
|
sizePos = ebmlFile.tell()
|
||||||
|
ebmlFile.write(encoding.encodeSize(None, sl))
|
||||||
|
size = 0
|
||||||
|
for chEl in xmlEl:
|
||||||
|
size += xmlElement2ebml(chEl, ebmlFile, schema, sl)
|
||||||
|
endPos = ebmlFile.tell()
|
||||||
|
ebmlFile.seek(sizePos)
|
||||||
|
ebmlFile.write(encoding.encodeSize(size, sl))
|
||||||
|
ebmlFile.seek(endPos)
|
||||||
|
return len(encId) + (endPos - sizePos)
|
||||||
|
|
||||||
|
elif issubclass(cls, core.BinaryElement):
|
||||||
|
val = xml_codecs.BINARY_CODECS[codec].decode(xmlEl.text)
|
||||||
|
elif issubclass(cls, (core.IntegerElement, core.FloatElement)):
|
||||||
|
val = ast.literal_eval(xmlEl.get('value'))
|
||||||
|
else:
|
||||||
|
val = cls.dtype(xmlEl.get('value'))
|
||||||
|
|
||||||
|
size = xmlEl.get('size', None)
|
||||||
|
if size is not None:
|
||||||
|
size = int(size)
|
||||||
|
sl = xmlEl.get('sizeLength')
|
||||||
|
if sl is not None:
|
||||||
|
sl = int(sl)
|
||||||
|
|
||||||
|
encoded = cls.encode(val, size, lengthSize=sl)
|
||||||
|
ebmlFile.write(encoded)
|
||||||
|
return len(encoded)
|
||||||
|
|
||||||
|
|
||||||
|
def xml2ebml(xmlFile, ebmlFile, schema, sizeLength=None, headers=True,
|
||||||
|
unknown=True):
|
||||||
|
""" Convert an XML file to EBML.
|
||||||
|
|
||||||
|
@todo: Convert XML on the fly, rather than parsing it first, allowing
|
||||||
|
for the conversion of arbitrarily huge files.
|
||||||
|
|
||||||
|
@param xmlFile: The XML source. Can be a filename, an open file-like
|
||||||
|
stream, or a parsed XML document.
|
||||||
|
@param ebmlFile: The EBML file to write. Can be a filename or an open
|
||||||
|
file-like stream.
|
||||||
|
@param schema: The EBML schema to use. Can be a filename or an
|
||||||
|
instance of a `Schema`.
|
||||||
|
@keyword sizeLength: The default length of each element's size
|
||||||
|
descriptor. Must be large enough to store the largest 'master'
|
||||||
|
element. If an XML element has a ``sizeLength`` attribute, it will
|
||||||
|
override this.
|
||||||
|
@keyword headers: If `True`, generate the standard ``EBML`` EBML
|
||||||
|
element if the XML document does not contain one.
|
||||||
|
@param unknown: If `True`, unknown element names will be allowed,
|
||||||
|
provided their XML elements include an ``id`` attribute with the
|
||||||
|
EBML ID (in hexadecimal).
|
||||||
|
@return: the size of the ebml file in bytes.
|
||||||
|
@raise NameError: raises if an xml element is not present in the schema.
|
||||||
|
"""
|
||||||
|
if isinstance(ebmlFile, (str, bytes, bytearray)):
|
||||||
|
ebmlFile = open(ebmlFile, 'wb')
|
||||||
|
openedEbml = True
|
||||||
|
else:
|
||||||
|
openedEbml = False
|
||||||
|
|
||||||
|
if not isinstance(schema, core.Schema):
|
||||||
|
schema = core.loadSchema(schema)
|
||||||
|
|
||||||
|
if isinstance(xmlFile, ET.Element):
|
||||||
|
# Already a parsed XML element
|
||||||
|
xmlRoot = xmlFile
|
||||||
|
elif isinstance(xmlFile, ET.ElementTree):
|
||||||
|
# Already a parsed XML document
|
||||||
|
xmlRoot = xmlFile.getroot()
|
||||||
|
else:
|
||||||
|
xmlDoc = ET.parse(xmlFile)
|
||||||
|
xmlRoot = xmlDoc.getroot()
|
||||||
|
|
||||||
|
if xmlRoot.tag not in schema and xmlRoot.tag != schema.document.__name__:
|
||||||
|
raise NameError("XML element %s not an element or document in "
|
||||||
|
"schema %s (wrong schema)" % (xmlRoot.tag, schema.name))
|
||||||
|
|
||||||
|
headers = headers and 'EBML' in schema
|
||||||
|
if headers and 'EBML' not in (el.tag for el in xmlRoot):
|
||||||
|
pos = ebmlFile.tell()
|
||||||
|
cls = schema.document
|
||||||
|
ebmlFile.write(cls.encodePayload(cls._createHeaders()))
|
||||||
|
numBytes = ebmlFile.tell() - pos
|
||||||
|
else:
|
||||||
|
numBytes = 0
|
||||||
|
|
||||||
|
if xmlRoot.tag == schema.document.__name__:
|
||||||
|
for el in xmlRoot:
|
||||||
|
numBytes += xmlElement2ebml(el, ebmlFile, schema, sizeLength,
|
||||||
|
unknown=unknown)
|
||||||
|
else:
|
||||||
|
numBytes += xmlElement2ebml(xmlRoot, ebmlFile, schema, sizeLength,
|
||||||
|
unknown=unknown)
|
||||||
|
|
||||||
|
if openedEbml:
|
||||||
|
ebmlFile.close()
|
||||||
|
|
||||||
|
return numBytes
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
#
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def loadXml(xmlFile, schema, ebmlFile=None):
|
||||||
|
""" Helpful utility to load an EBML document from an XML file.
|
||||||
|
|
||||||
|
@param xmlFile: The XML source. Can be a filename, an open file-like
|
||||||
|
stream, or a parsed XML document.
|
||||||
|
@param schema: The EBML schema to use. Can be a filename or an
|
||||||
|
instance of a `Schema`.
|
||||||
|
@keyword ebmlFile: The name of the temporary EBML file to write, or
|
||||||
|
``:memory:`` to use RAM (like `sqlite3`). Defaults to an
|
||||||
|
automatically-generated temporary file.
|
||||||
|
@return The root node of the specified EBML file.
|
||||||
|
"""
|
||||||
|
if ebmlFile == ":memory:":
|
||||||
|
ebmlFile = StringIO()
|
||||||
|
xml2ebml(xmlFile, ebmlFile, schema)
|
||||||
|
ebmlFile.seek(0)
|
||||||
|
else:
|
||||||
|
ebmlFile = tempfile.mktemp() if ebmlFile is None else ebmlFile
|
||||||
|
xml2ebml(xmlFile, ebmlFile, schema)
|
||||||
|
|
||||||
|
return schema.load(ebmlFile)
|
||||||
|
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
#
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
def pprint(el, values=True, out=sys.stdout, indent=" ", binary_codec="ignore",
|
||||||
|
void_codec="ignore", _depth=0):
|
||||||
|
""" Test function to recursively crawl an EBML document or element and
|
||||||
|
print its structure, with child elements shown indented.
|
||||||
|
|
||||||
|
@param el: An instance of a `Document` or `Element` subclass.
|
||||||
|
@keyword values: If `True`, show elements' values.
|
||||||
|
@keyword out: A file-like stream to which to write.
|
||||||
|
@keyword indent: The string containing the character(s) used for each
|
||||||
|
indentation.
|
||||||
|
@keyword binary_codec: The name of a class from `ebmlite.xml_codecs`,
|
||||||
|
or an instance of a codec, for rendering binary elements as text.
|
||||||
|
@keyword void_codec: The name of a class from `ebmlite.xml_codecs`,
|
||||||
|
or an instance of a codec, for rendering the contents of Void
|
||||||
|
elements as text.
|
||||||
|
"""
|
||||||
|
tab = indent * _depth
|
||||||
|
|
||||||
|
if isinstance(binary_codec, str):
|
||||||
|
binary_codec = xml_codecs.BINARY_CODECS[binary_codec]()
|
||||||
|
if isinstance(void_codec, str):
|
||||||
|
void_codec = xml_codecs.BINARY_CODECS[void_codec]()
|
||||||
|
|
||||||
|
if _depth == 0:
|
||||||
|
if values:
|
||||||
|
out.write("Offset Size Element (ID): Value\n")
|
||||||
|
else:
|
||||||
|
out.write("Offset Size Element (ID)\n")
|
||||||
|
out.write("====== ====== =================================\n")
|
||||||
|
|
||||||
|
if isinstance(el, core.Document):
|
||||||
|
out.write("%06s %06s %s %s (Document, type %s)\n" % (el.offset, el.size, tab, el.name, el.type))
|
||||||
|
for i in el:
|
||||||
|
pprint(i, values, out, indent, binary_codec, void_codec, _depth+1)
|
||||||
|
else:
|
||||||
|
out.write("%06s %06s %s %s (ID 0x%0X)" % (el.offset, el.size, tab, el.name, el.id))
|
||||||
|
if isinstance(el, core.MasterElement):
|
||||||
|
out.write(": (master) %d subelements\n" % len(el.value))
|
||||||
|
for i in el:
|
||||||
|
pprint(i, values, out, indent, binary_codec, void_codec, _depth+1)
|
||||||
|
else:
|
||||||
|
out.write(": (%s)" % el.dtype.__name__)
|
||||||
|
if values:
|
||||||
|
if isinstance(el, core.BinaryElement):
|
||||||
|
indent = tab + " " * 17
|
||||||
|
if isinstance(el, core.VoidElement) and void_codec.NAME != 'ignore':
|
||||||
|
out.write(" <{}>".format(void_codec.NAME))
|
||||||
|
void_codec.encode(el.value, offset=el.offset, indent=indent, stream=out)
|
||||||
|
elif binary_codec.NAME != 'ignore':
|
||||||
|
out.write(" <{}>".format(binary_codec.NAME))
|
||||||
|
binary_codec.encode(el.value, offset=el.offset, indent=indent, stream=out)
|
||||||
|
else:
|
||||||
|
out.write(" %r" % (el.value))
|
||||||
|
out.write("\n")
|
||||||
|
|
||||||
|
out.flush()
|
||||||
|
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
#
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
def printSchemata(paths=None, out=sys.stdout, absolute=True):
|
||||||
|
""" Display a list of schemata in `SCHEMA_PATH`. A thin wrapper for the
|
||||||
|
core `listSchemata()` function.
|
||||||
|
|
||||||
|
@param out: A file-like stream to which to write.
|
||||||
|
"""
|
||||||
|
out = out or sys.stdout
|
||||||
|
newfile = isinstance(out, (str, pathlib.Path))
|
||||||
|
if newfile:
|
||||||
|
out = open(out, 'w')
|
||||||
|
|
||||||
|
try:
|
||||||
|
if paths:
|
||||||
|
paths.extend(core.SCHEMA_PATH)
|
||||||
|
else:
|
||||||
|
paths = core.SCHEMA_PATH
|
||||||
|
schemata = core.listSchemata(*paths, absolute=absolute)
|
||||||
|
for k, v in schemata.items():
|
||||||
|
out.write("{}\n".format(k))
|
||||||
|
for s in v:
|
||||||
|
out.write(" {}\n".format(s))
|
||||||
|
out.flush()
|
||||||
|
finally:
|
||||||
|
if newfile:
|
||||||
|
out.close()
|
||||||
305
lambda/cctv-people-rekognition/ebmlite/xml_codecs.py
Normal file
305
lambda/cctv-people-rekognition/ebmlite/xml_codecs.py
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
"""
|
||||||
|
Classes for various means of encoding/decoding binary data to/from XML.
|
||||||
|
|
||||||
|
Note: the class docstrings will be shown in the `ebml2xml` help text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from io import BytesIO, StringIO
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class BinaryCodec:
|
||||||
|
""" Base class for binary encoders/decoders, rendering and reading
|
||||||
|
`BinaryElement` contents as text.
|
||||||
|
|
||||||
|
:cvar NAME: The codec's name, written to the rendered XML as
|
||||||
|
the `encoding` attribute. Also used as the `--encoding`
|
||||||
|
argument in the command-line tools. Must be unique, and
|
||||||
|
should be lowercase.
|
||||||
|
:type NAME: str
|
||||||
|
"""
|
||||||
|
NAME = ""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
""" Constructor. All arguments should be optional keyword
|
||||||
|
arguments. Can be considered optional in subclasses.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def encode(self, data, stream=None, indent='', offset=0, **kwargs):
|
||||||
|
""" Convert binary data to text. Typical arguments:
|
||||||
|
|
||||||
|
:param data: The binary data from an EBML `BinaryElement`.
|
||||||
|
:param stream: An optional stream to which to write the encoded
|
||||||
|
data. Should be included and used in all implementations.
|
||||||
|
:param indent: Indentation before each row of text. Used if
|
||||||
|
the codec was instantiated with `cols` specified.
|
||||||
|
:param offset: The originating EBML element's offset in the file.
|
||||||
|
For use with codecs that write line numbers/position info.
|
||||||
|
:returns: If no `stream`, the encoded data as text. If `stream`,
|
||||||
|
the number of bytes written.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def decode(cls, data, stream=None):
|
||||||
|
""" Decode binary data in text form (e.g., from an XML file). Note:
|
||||||
|
this is a `classmethod`, and should work regardless of the
|
||||||
|
arguments used when the data was encoded (e.g., with or without
|
||||||
|
indentations and/or line breaks, metadata like offsets, etc.).
|
||||||
|
|
||||||
|
:param data: The text data from an XML file.
|
||||||
|
:param stream: A stream to which to write the encoded data.
|
||||||
|
:returns: If no `stream`, the decoded binary data. If `stream`,
|
||||||
|
the number of bytes written.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class Base64Codec(BinaryCodec):
|
||||||
|
""" Encoder/decoder for binary data as base64 formatted text to/from text.
|
||||||
|
"""
|
||||||
|
NAME = "base64"
|
||||||
|
|
||||||
|
def __init__(self, cols=76, **kwargs):
|
||||||
|
""" Constructor.
|
||||||
|
|
||||||
|
:param cols: The length of each line of base64 data, excluding
|
||||||
|
any indentation specified when encoding. If 0 or `None`,
|
||||||
|
data will be written as a single continuous block with no
|
||||||
|
newlines.
|
||||||
|
|
||||||
|
Additional keyword arguments will be accepted (to maintain
|
||||||
|
compatibility with other codecs) but ignored.
|
||||||
|
"""
|
||||||
|
self.cols = cols
|
||||||
|
|
||||||
|
|
||||||
|
def encode(self, data, stream=None, indent='', **kwargs):
|
||||||
|
""" Convert binary data to base64 text.
|
||||||
|
|
||||||
|
:param data: The binary data from an EBML `BinaryElement`.
|
||||||
|
:param stream: An optional stream to which to write the encoded
|
||||||
|
data.
|
||||||
|
:param indent: Indentation before each row of text. Used if
|
||||||
|
the codec was instantiated with `cols` specified.
|
||||||
|
:returns: If no `stream`, the encoded data as text. If `stream`,
|
||||||
|
the number of bytes written.
|
||||||
|
|
||||||
|
Additional keyword arguments will be accepted (to maintain
|
||||||
|
compatibility with other codecs) but ignored.
|
||||||
|
"""
|
||||||
|
if isinstance(indent, bytes):
|
||||||
|
indent = indent.decode()
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.encode('utf8')
|
||||||
|
|
||||||
|
result = base64.encodebytes(data).decode()
|
||||||
|
if stream is None:
|
||||||
|
out = StringIO()
|
||||||
|
else:
|
||||||
|
out = stream
|
||||||
|
|
||||||
|
if self.cols == 76:
|
||||||
|
# Default width of a base64 line; use existing newlines
|
||||||
|
result = "\n" + result
|
||||||
|
if indent:
|
||||||
|
result = result.replace('\n', '\n' + indent)
|
||||||
|
if stream is not None:
|
||||||
|
return out.write(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
result = result.replace('\n', '')
|
||||||
|
|
||||||
|
if self.cols is None:
|
||||||
|
if stream is not None:
|
||||||
|
return out.write(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
numbytes = 0
|
||||||
|
for chunk in range(0, len(result), self.cols):
|
||||||
|
numbytes += out.write('\n')
|
||||||
|
numbytes += out.write(indent) + out.write(result[chunk:chunk+self.cols])
|
||||||
|
|
||||||
|
if stream is None:
|
||||||
|
return out.getvalue()
|
||||||
|
|
||||||
|
return numbytes
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def decode(cls, data, stream=None):
|
||||||
|
""" Decode binary data in base64 (e.g., from an XML file). Note: this
|
||||||
|
is a `classmethod`, and works regardles of how the encoded data was
|
||||||
|
formatted (e.g., with indentations and/or line breaks).
|
||||||
|
|
||||||
|
:param data: The base64 data from an XML file.
|
||||||
|
:param stream: A stream to which to write the encoded data.
|
||||||
|
:returns: If no `stream`, the decoded binary data. If `stream`,
|
||||||
|
the number of bytes written.
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
if stream is None:
|
||||||
|
return b''
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.encode('utf8')
|
||||||
|
|
||||||
|
result = base64.decodebytes(data)
|
||||||
|
|
||||||
|
if stream is not None:
|
||||||
|
return stream.write(result)
|
||||||
|
else:
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class HexCodec(BinaryCodec):
|
||||||
|
""" Encoder/decoder for binary data as hexadecimal format to/from text.
|
||||||
|
Encoded text is multiple columns of bytes/words (default is 16 columns,
|
||||||
|
2 bytes per column), with an optional file offset at the start of each
|
||||||
|
row.
|
||||||
|
"""
|
||||||
|
# The name shown in the encoded XML element's `encoding` attribute
|
||||||
|
NAME = "hex"
|
||||||
|
|
||||||
|
def __init__(self, width=2, cols=32, offsets=True, **kwargs):
|
||||||
|
""" Constructor.
|
||||||
|
|
||||||
|
:param width: The number of bytes displayed per column when
|
||||||
|
encoding.
|
||||||
|
:param cols: The number of columns to display when encoding. If 0
|
||||||
|
or `None`, data will be written as a single continuous block
|
||||||
|
with no newlines.
|
||||||
|
:param offsets: If `True`, each line will start with its offset
|
||||||
|
(in decimal). Applicable if `cols` is a non-zero number.
|
||||||
|
"""
|
||||||
|
self.width = width
|
||||||
|
self.cols = cols
|
||||||
|
self.offsets = bool(offsets and cols)
|
||||||
|
|
||||||
|
|
||||||
|
def encode(self, data, stream=None, offset=0, indent='', **kwargs):
|
||||||
|
""" Convert binary data to hexadecimal text.
|
||||||
|
|
||||||
|
:param data: The binary data from an EBML `BinaryElement`.
|
||||||
|
:param stream: An optional stream to which to write the encoded
|
||||||
|
data.
|
||||||
|
:param offset: A starting number for the displayed offsets column.
|
||||||
|
For showing the data's offset in an EBML file.
|
||||||
|
:param indent: Indentation before each row of hex text.
|
||||||
|
:returns: If no `stream`, the encoded data as text. If `stream`,
|
||||||
|
the number of bytes written.
|
||||||
|
"""
|
||||||
|
if not isinstance(indent, str):
|
||||||
|
indent = indent.decode()
|
||||||
|
|
||||||
|
if stream is None:
|
||||||
|
out = StringIO()
|
||||||
|
else:
|
||||||
|
out = stream
|
||||||
|
|
||||||
|
newline = bool(self.cols)
|
||||||
|
offsets = self.offsets and newline
|
||||||
|
|
||||||
|
numbytes = 0
|
||||||
|
for i, b in enumerate(data):
|
||||||
|
if newline and not i % self.cols:
|
||||||
|
numbytes += out.write('\n')
|
||||||
|
numbytes += out.write(indent)
|
||||||
|
if offsets:
|
||||||
|
numbytes += out.write('[{:06d}] '.format(i + offset))
|
||||||
|
elif not i % self.width:
|
||||||
|
numbytes += out.write(' ')
|
||||||
|
numbytes += out.write('{:02x}'.format(b))
|
||||||
|
|
||||||
|
if stream is None:
|
||||||
|
return out.getvalue()
|
||||||
|
|
||||||
|
return numbytes
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def decode(cls, data, stream=None):
|
||||||
|
""" Decode binary data in hexadecimal (e.g., from an XML file). Note:
|
||||||
|
this is a `classmethod`, and works regardles of how the encoded
|
||||||
|
data was formatted (e.g., number of columns, with or without
|
||||||
|
offsets, etc.).
|
||||||
|
|
||||||
|
:param data: The base64 data from an XML file.
|
||||||
|
:param stream: A stream to which to write the encoded data.
|
||||||
|
:returns: If no `stream`, the decoded binary data. If `stream`,
|
||||||
|
the number of bytes written.
|
||||||
|
"""
|
||||||
|
if stream is None:
|
||||||
|
out = BytesIO()
|
||||||
|
else:
|
||||||
|
out = stream
|
||||||
|
numbytes = 0
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
if stream is None:
|
||||||
|
return b''
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.encode('utf8')
|
||||||
|
|
||||||
|
for word in data.split():
|
||||||
|
if b'[' in word or b']' in word:
|
||||||
|
continue
|
||||||
|
for i in range(0, len(word), 2):
|
||||||
|
numbytes += out.write((int(word[i:i+2], 16).to_bytes(1, 'big')))
|
||||||
|
|
||||||
|
if stream is None:
|
||||||
|
return out.getvalue()
|
||||||
|
|
||||||
|
return numbytes
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class IgnoreCodec(BinaryCodec):
|
||||||
|
""" Suppresses writing binary data as text.
|
||||||
|
"""
|
||||||
|
NAME = "ignore"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def encode(data, stream=None, **kwargs):
|
||||||
|
if stream:
|
||||||
|
return 0
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode(data, stream=None, **kwargs):
|
||||||
|
if stream:
|
||||||
|
return 0
|
||||||
|
return b''
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Collection of codecs. The first one will be the default in the CLI (or at least
|
||||||
|
# it will be in Python 3.7 and later). User-implemented codecs should be added to
|
||||||
|
# the dictionary.
|
||||||
|
BINARY_CODECS = {'base64': Base64Codec,
|
||||||
|
'hex': HexCodec,
|
||||||
|
'ignore': IgnoreCodec}
|
||||||
@ -0,0 +1,387 @@
|
|||||||
|
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
# SPDX-License-Identifier: MIT-0.
|
||||||
|
|
||||||
|
'''
|
||||||
|
Amazon Kinesis Video Stream (KVS) Consumer Library for Python.
|
||||||
|
|
||||||
|
This class provides post-processing fiunctions for a MKV fragement that has been parsed
|
||||||
|
by the Amazon Kinesis Video Streams Cosumer Library for Python.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
__version__ = "0.0.1"
|
||||||
|
__status__ = "Development"
|
||||||
|
__copyright__ = "Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved."
|
||||||
|
__author__ = "Dean Colcott <https://www.linkedin.com/in/deancolcott/>"
|
||||||
|
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import ebmlite.util as emblite_utils
|
||||||
|
import wave
|
||||||
|
import ebmlite.decoding as ebmlite_decoding
|
||||||
|
|
||||||
|
# Init the logger.
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class KvsFragementProcessor():
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# Fragment processing functions
|
||||||
|
|
||||||
|
def get_fragment_tags(self, fragment_dom):
|
||||||
|
'''
|
||||||
|
Parses a MKV Fragment Doc (of type ebmlite.core.MatroskaDocument) that is returned to the provided callback
|
||||||
|
from get_streaming_fragments() in this class and returns a dict of the SimpleTag elements found.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
**fragment_dom**: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
### Returns:
|
||||||
|
|
||||||
|
simple_tags: dict
|
||||||
|
|
||||||
|
Dictionary of all SimpleTag elements with format - TagName<String> : TagValue <String | Binary>.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Get the Segment Element of the Fragment DOM - error if not found
|
||||||
|
segment_element = None
|
||||||
|
for element in fragment_dom:
|
||||||
|
if (element.id == 0x18538067): # MKV Segment Element ID
|
||||||
|
segment_element = element
|
||||||
|
break
|
||||||
|
|
||||||
|
if (not segment_element):
|
||||||
|
raise KeyError('Segment Element required but not found in fragment_doc' )
|
||||||
|
|
||||||
|
# Save all of the SimpleTag elements in the Segment element
|
||||||
|
simple_tag_elements = []
|
||||||
|
for element in segment_element:
|
||||||
|
if (element.id == 0x1254C367): # Tags element type ID
|
||||||
|
for tags in element:
|
||||||
|
if (tags.id == 0x7373): # Tag element type ID
|
||||||
|
for tag_type in tags:
|
||||||
|
if (tag_type.id == 0x67C8 ): # SimpleTag element type ID
|
||||||
|
simple_tag_elements.append(tag_type)
|
||||||
|
|
||||||
|
# For all SimpleTags types (ID: 0x67C8), save for TagName (ID: 0x7373) and values of TagString (ID:0x4487) or TagBinary (ID: 0x4485 )
|
||||||
|
simple_tags_dict = {}
|
||||||
|
for simple_tag in simple_tag_elements:
|
||||||
|
|
||||||
|
tag_name = None
|
||||||
|
tag_value = None
|
||||||
|
for element in simple_tag:
|
||||||
|
if (element.id == 0x45A3): # Tag Name element type ID
|
||||||
|
tag_name = element.value
|
||||||
|
elif (element.id == 0x4487 or element.id == 0x4485): # TagString and TagBinary element type IDs respectively
|
||||||
|
tag_value = element.value
|
||||||
|
|
||||||
|
# As long as tag name was found add the Tag to the return dict.
|
||||||
|
if (tag_name):
|
||||||
|
simple_tags_dict[tag_name] = tag_value
|
||||||
|
|
||||||
|
return simple_tags_dict
|
||||||
|
|
||||||
|
def get_fragement_dom_pretty_string(self, fragment_dom):
|
||||||
|
'''
|
||||||
|
Returns the Pretty Print parsing of the EBMLite fragment DOM as a string
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
**fragment_dom**: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
### Return:
|
||||||
|
**pretty_print_str**: str
|
||||||
|
Pretty print string of the Fragment DOM object
|
||||||
|
'''
|
||||||
|
|
||||||
|
pretty_print_str = io.StringIO()
|
||||||
|
|
||||||
|
emblite_utils.pprint(fragment_dom, out=pretty_print_str)
|
||||||
|
return pretty_print_str.getvalue()
|
||||||
|
|
||||||
|
def save_fragment_as_local_mkv(self, fragment_bytes, file_name_path):
|
||||||
|
'''
|
||||||
|
Save the provided fragment_bytes as stand-alone MKV file on local disk.
|
||||||
|
fragment_bytes as it arrives in is already a well formatted MKV fragment
|
||||||
|
so can just write the bytes straight to disk and it will be a playable MKV file.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
fragment_bytes: bytearray
|
||||||
|
A ByteArray with raw bytes from exactly one fragment.
|
||||||
|
|
||||||
|
file_name_path: Str
|
||||||
|
Local file path / name to save the MKV file to.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
f = open(file_name_path, "wb")
|
||||||
|
f.write(fragment_bytes)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def get_frames_as_ndarray(self, fragment_bytes, one_in_frames_ratio):
|
||||||
|
'''
|
||||||
|
Parses fragment_bytes and returns a ratio of available frames in the MKV fragment as
|
||||||
|
a list of numpy.ndarray's.
|
||||||
|
|
||||||
|
e.g: Setting one_in_frames_ratio = 5 will return every 5th frame found in the fragment.
|
||||||
|
(Starting with the first)
|
||||||
|
|
||||||
|
To return all available frames just set one_in_frames_ratio = 1
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
fragment_bytes: bytearray
|
||||||
|
A ByteArray with raw bytes from exactly one fragment.
|
||||||
|
|
||||||
|
one_in_frames_ratio: Str
|
||||||
|
Ratio of the available frames in the fragment to process and return.
|
||||||
|
|
||||||
|
### Return:
|
||||||
|
|
||||||
|
frames: List<numpy.ndarray>
|
||||||
|
A list of frames extracted from the fragment as numpy.ndarray
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Parse all frames in the fragment to frames list
|
||||||
|
frames = iio.imread(io.BytesIO(fragment_bytes), plugin="pyav", index=...)
|
||||||
|
|
||||||
|
# Store and return frames in frame ratio of total available
|
||||||
|
ret_frames = []
|
||||||
|
for i in range(0, len(frames), one_in_frames_ratio):
|
||||||
|
ret_frames.append(frames[i])
|
||||||
|
|
||||||
|
return ret_frames
|
||||||
|
|
||||||
|
def save_frames_as_jpeg(self, fragment_bytes, one_in_frames_ratio, jpg_file_base_path):
|
||||||
|
'''
|
||||||
|
Parses fragment_bytes and saves a ratio of available frames in the MKV fragment as
|
||||||
|
JPEGs on the local disk.
|
||||||
|
|
||||||
|
e.g: Setting one_in_frames_ratio = 5 will return every 5th frame found in the fragment
|
||||||
|
(starting with the first).
|
||||||
|
|
||||||
|
To return all available frames just set one_in_frames_ratio = 1
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
fragment_bytes: ByteArray
|
||||||
|
A ByteArray with raw bytes from exactly one fragment.
|
||||||
|
|
||||||
|
one_in_frames_ratio: Str
|
||||||
|
Ratio of the available frames in the fragment to process and save.
|
||||||
|
|
||||||
|
### Return
|
||||||
|
jpeg_paths : List<Str>
|
||||||
|
A list of file paths to the saved JPEN files.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Parse all frames in the fragment to frames list
|
||||||
|
ndarray_frames = self.get_frames_as_ndarray(fragment_bytes, one_in_frames_ratio)
|
||||||
|
|
||||||
|
# Write frames to disk as JPEG images
|
||||||
|
jpeg_paths = []
|
||||||
|
for i in range(len(ndarray_frames)):
|
||||||
|
frame = ndarray_frames[i]
|
||||||
|
image_file_path = '{}-{}.jpg'.format(jpg_file_base_path, i)
|
||||||
|
iio.imwrite(image_file_path, frame, format=None)
|
||||||
|
jpeg_paths.append(image_file_path)
|
||||||
|
|
||||||
|
return jpeg_paths
|
||||||
|
|
||||||
|
|
||||||
|
def get_raw_audio_track_from_simple_block(self, mkv_element):
|
||||||
|
'''
|
||||||
|
This function gets the raw audio track from a SimpleBlock element
|
||||||
|
in a Matroska file from Amazon Connect.
|
||||||
|
|
||||||
|
It will remove SimpleBlock header as per:
|
||||||
|
https://github.com/ietf-wg-cellar/matroska-specification/blob/master/notes.md
|
||||||
|
|
||||||
|
Will works only if track number VINT is one octet length.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
mkv_element: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
### Return:
|
||||||
|
A bytearray containing the raw audio data of the specified track
|
||||||
|
'''
|
||||||
|
|
||||||
|
if mkv_element.name == "SimpleBlock":
|
||||||
|
mkv_element.stream.seek(mkv_element.payloadOffset+4)
|
||||||
|
return mkv_element.parse(mkv_element.stream, mkv_element.size-4)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_audio_track_number_from_simple_block(self, mkv_element):
|
||||||
|
'''
|
||||||
|
This function gets the number of audio track from a SimpleBlock element
|
||||||
|
in a Matroska file from Amazon Connect.
|
||||||
|
|
||||||
|
Will works only if track number VINT is one octet length as per:
|
||||||
|
https://github.com/ietf-wg-cellar/matroska-specification/blob/master/notes.md
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
mkv_element: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
### Return:
|
||||||
|
number of audio track in SimpleBlock
|
||||||
|
'''
|
||||||
|
|
||||||
|
if mkv_element.name == "SimpleBlock":
|
||||||
|
mkv_element.stream.seek(mkv_element.payloadOffset)
|
||||||
|
ch = mkv_element.stream.read(1)
|
||||||
|
length, _ = ebmlite_decoding.decodeIntLength(ord(ch))
|
||||||
|
if length == 1:
|
||||||
|
'''
|
||||||
|
removing VINT_MARKER as per https://datatracker.ietf.org/doc/rfc8794/ paragraph 4
|
||||||
|
'''
|
||||||
|
track_nr = ord(ch) & 127
|
||||||
|
|
||||||
|
return track_nr
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_track_bytearray(self, mkv_dom, track_nr):
|
||||||
|
'''
|
||||||
|
This function extracts the raw audio track from a Matroska
|
||||||
|
file from Amazon Connect and returns it as a bytearray. It iterates through
|
||||||
|
the SimpleBlock elements within each Cluster, alternating which
|
||||||
|
track it appends based on the track number.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
mkv_dom: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
track_nr: The track number (1 or 2) to extract
|
||||||
|
|
||||||
|
### Return:
|
||||||
|
A bytearray containing the raw audio data of the specified track
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
track_bytearray = bytearray()
|
||||||
|
|
||||||
|
for element in mkv_dom:
|
||||||
|
for segment_child in element:
|
||||||
|
if segment_child.name == "Cluster":
|
||||||
|
i=0
|
||||||
|
for cluster_child in segment_child:
|
||||||
|
if cluster_child.name == "SimpleBlock":
|
||||||
|
simple_block_track_nr =self.get_audio_track_number_from_simple_block(cluster_child)
|
||||||
|
i+=1
|
||||||
|
if track_nr == simple_block_track_nr:
|
||||||
|
track_bytearray.extend(self.get_raw_audio_track_from_simple_block(cluster_child))
|
||||||
|
|
||||||
|
return track_bytearray
|
||||||
|
|
||||||
|
def get_track_number_by_name(self, fragment_dom, track_name):
|
||||||
|
'''
|
||||||
|
This function gets the track number from a Amazon Connect Matroska fragment
|
||||||
|
by track name.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
fragment_dom: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
track_name (str): The name of the track to lookup.
|
||||||
|
|
||||||
|
### Returns:
|
||||||
|
int: The track number (as an integer), or None if not found.
|
||||||
|
'''
|
||||||
|
for element in fragment_dom:
|
||||||
|
|
||||||
|
for segment_child in element:
|
||||||
|
|
||||||
|
if segment_child.name == "Tracks":
|
||||||
|
for cluster_child in segment_child:
|
||||||
|
fragment_dom_track_name = ''
|
||||||
|
fragment_dom_track_number = 0
|
||||||
|
if cluster_child.name == "TrackEntry":
|
||||||
|
for te_child in cluster_child:
|
||||||
|
if te_child.name == "Name":
|
||||||
|
fragment_dom_track_name = te_child.value
|
||||||
|
if te_child.name == "TrackNumber":
|
||||||
|
fragment_dom_track_number = te_child.value
|
||||||
|
if fragment_dom_track_name == track_name:
|
||||||
|
return fragment_dom_track_number
|
||||||
|
return None
|
||||||
|
|
||||||
|
def convert_track_to_wav(self, track_bytearray):
|
||||||
|
'''
|
||||||
|
This function converts a track bytearray to a wav file.
|
||||||
|
'''
|
||||||
|
|
||||||
|
file_wav = io.BytesIO()
|
||||||
|
with wave.open(file_wav, 'wb') as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setframerate(8000)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.writeframes(track_bytearray)
|
||||||
|
return file_wav
|
||||||
|
|
||||||
|
def save_connect_fragment_audio_track_as_wav(self, fragment_dom, track_nr, file_name_path):
|
||||||
|
'''
|
||||||
|
Save the provided fragment_dom as wav file on local disk.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
fragment_dom: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
tranck_nr: int
|
||||||
|
The track number (1 or 2) to extract
|
||||||
|
|
||||||
|
file_name_path: Str
|
||||||
|
Local file path / name to save the MKV file to.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
fragment_bytes = self.get_track_bytearray(fragment_dom, track_nr)
|
||||||
|
fragment_wav = self.convert_track_to_wav(fragment_bytes)
|
||||||
|
with open(file_name_path, 'wb') as f:
|
||||||
|
f.write(fragment_wav.getvalue())
|
||||||
|
|
||||||
|
def save_connect_fragment_audio_track_from_customer_as_wav(self, fragment_dom, file_name_path_part):
|
||||||
|
'''
|
||||||
|
Saves the audio track from the customer in a Amazon Connect Matroska fragment
|
||||||
|
as a WAV file.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
fragment_dom: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
file_name_path_part (str): The file path to save the WAV file to
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
track_number = self.get_track_number_by_name(fragment_dom, "AUDIO_FROM_CUSTOMER")
|
||||||
|
if track_number:
|
||||||
|
file_name_path = file_name_path_part + "-AUDIO_FROM_CUSTOMER.wav"
|
||||||
|
self.save_connect_fragment_audio_track_as_wav(fragment_dom, track_number, file_name_path)
|
||||||
|
|
||||||
|
def save_connect_fragment_audio_track_to_customer_as_wav(self, fragment_dom, file_name_path_part):
|
||||||
|
'''
|
||||||
|
Saves the audio track to the customer in a Amazon Connect Matroska fragment
|
||||||
|
as a WAV file.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
fragment_dom: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
file_name_path_part (str): The file path to save the WAV file to
|
||||||
|
|
||||||
|
'''
|
||||||
|
track_number = self.get_track_number_by_name(fragment_dom, "AUDIO_TO_CUSTOMER")
|
||||||
|
if track_number:
|
||||||
|
file_name_path = file_name_path_part + "-AUDIO_TO_CUSTOMER.wav"
|
||||||
|
self.save_connect_fragment_audio_track_as_wav(fragment_dom, track_number, file_name_path)
|
||||||
227
lambda/cctv-people-rekognition/kinesis_video_streams_parser.py
Normal file
227
lambda/cctv-people-rekognition/kinesis_video_streams_parser.py
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
# SPDX-License-Identifier: MIT-0.
|
||||||
|
|
||||||
|
'''
|
||||||
|
Amazon Kinesis Video Stream (KVS) Consumer Library for Python.
|
||||||
|
|
||||||
|
This library parses streaming bytes (chunks) made available by the StreamingBody returned from calls
|
||||||
|
to the KVS Media Client GetMedia and KVS Archive Media Client GetMediaForFragmentList
|
||||||
|
API.
|
||||||
|
|
||||||
|
The Amazon Kinesis Video Stream (KVS) Consumer Library for Python reads in streaming bytes as they become
|
||||||
|
available and parses to individual MKV fragments. The library is threaded and non-blocking,
|
||||||
|
once a stream is being read it forwards received MKV fragments to named call-backs in the users application.
|
||||||
|
|
||||||
|
Fragments are returned as raw bytes and a searchable DOM like structure by parsing with EMBLite by MideTechnology.
|
||||||
|
|
||||||
|
The consumer library provides the following functions to further process parsed MKV fragments:
|
||||||
|
1) get_fragment_tags(): Extract MKV tags from the fragment.
|
||||||
|
2) save_fragment_as_local_mkv(): Saves the fragment as stand-alone MKV file on local disk.
|
||||||
|
3) get_frames_as_ndarray(): Returns a ratio of frames in the fragment as a list of NDArray objects.
|
||||||
|
4) save_frames_as_jpeg(): Returns a ratio of frames in the fragment as a JPEGs to local disk.
|
||||||
|
|
||||||
|
Workflow:
|
||||||
|
1) Define a on_fragment_arrived and on_read_stream_complete call-backs in user application logic. These to process
|
||||||
|
fragments as they are received and to handle the parser reaching the end of the stream. (When no more fragments are left),
|
||||||
|
2) Initialize the KVS Media and / or Archive Media clients,
|
||||||
|
3) Make a call to KVS Media GetMedia and / or KVS Archive Media GetMediaForFragmentList for the given stream,
|
||||||
|
4) Initialize this KVS Consumer library and call get_streaming_fragements providing the response from the GetMedia
|
||||||
|
or GetMediaForFragmentList call,
|
||||||
|
5) Fragments will then be parsed and delivered to the call-backs for processing as per the example code provided.
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
# EMBLite by MideTechnology is an external EBML parser found at https://github.com/MideTechnology/ebmlite
|
||||||
|
# For convenance a slightly modified version of EMBLite is shipped with the KvsConsumerLibrary but adding credit where its due.
|
||||||
|
# EMBLite MIT License: https://github.com/MideTechnology/ebmlite/blob/development/LICENSE
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
__version__ = "0.0.1"
|
||||||
|
__status__ = "Development"
|
||||||
|
__copyright__ = "Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved."
|
||||||
|
__author__ = "Dean Colcott <https://www.linkedin.com/in/deancolcott/>"
|
||||||
|
|
||||||
|
import timeit
|
||||||
|
import logging
|
||||||
|
from threading import Thread
|
||||||
|
from ebmlite import loadSchema
|
||||||
|
|
||||||
|
# Init the logger.
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class KvsConsumerLibrary(Thread):
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
stream_name,
|
||||||
|
get_media_response_object,
|
||||||
|
on_fragment_arrived,
|
||||||
|
on_read_stream_complete,
|
||||||
|
on_read_stream_exception):
|
||||||
|
'''
|
||||||
|
Initialize the KVS media consumer library
|
||||||
|
'''
|
||||||
|
# Call the Thread class's init function
|
||||||
|
Thread.__init__(self)
|
||||||
|
|
||||||
|
# Used to trigger graceful exit of this thread
|
||||||
|
self._stop_get_media = False
|
||||||
|
|
||||||
|
# Init the local vars.
|
||||||
|
log.info('Initilizing KvsConsumerLibrary...')
|
||||||
|
self.stream_name = stream_name
|
||||||
|
self.get_media_response_object = get_media_response_object
|
||||||
|
self.on_fragment_arrived_callback = on_fragment_arrived
|
||||||
|
self.on_read_stream_complete_callback = on_read_stream_complete
|
||||||
|
self.on_read_stream_exception = on_read_stream_exception
|
||||||
|
|
||||||
|
log.info('Loading EBMLlite MKV Schema....')
|
||||||
|
self.schema = loadSchema('matroska.xml')
|
||||||
|
|
||||||
|
def _get_ebml_header_elements(self, fragement_dom):
|
||||||
|
'''
|
||||||
|
Returns the EBML Header elements in the Fragment DOM. EBML Header elements indicate the start
|
||||||
|
of a new fragment and so we use them to set the byte boundaries of individual fragments as they
|
||||||
|
arrive in the raw data stream (chunks).
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
**fragment_dom**: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
'''
|
||||||
|
ebml_header_elements = []
|
||||||
|
# Iterate through the fragment elements and capture any EBML Fragment headers (indicating the start of a new fragment)
|
||||||
|
for element in fragement_dom:
|
||||||
|
if (element.id == 0x1A45DFA3): # EBML (Master) element ID = 0x1A45DFA3 (440786851 dec)
|
||||||
|
ebml_header_elements.append(element)
|
||||||
|
|
||||||
|
return ebml_header_elements
|
||||||
|
|
||||||
|
def _get_simple_block_elements(self, fragement_dom):
|
||||||
|
'''
|
||||||
|
Returns the DOM SimpleBlock elements found in the fragment.
|
||||||
|
SimpleBlock Elements store the payload of the MKV fragemeny - typically H.264/265 frames but
|
||||||
|
can be any data playload that was ingested by the KVS producer.
|
||||||
|
|
||||||
|
### Parameters:
|
||||||
|
|
||||||
|
**fragment_dom**: ebmlite.core.Document <ebmlite.core.MatroskaDocument>
|
||||||
|
The DOM like structure describing the fragment parsed by EBMLite.
|
||||||
|
|
||||||
|
'''
|
||||||
|
simple_block_elements = []
|
||||||
|
# Iterate through the fragment elements and capture any Simple Block type elements.
|
||||||
|
# These carry the fragments payload bytes (typically image frames as raw bytes.)
|
||||||
|
for element in fragement_dom:
|
||||||
|
if (element.id == 0x18538067): # Segment element ID = 0x18538067
|
||||||
|
|
||||||
|
for segement_child in element:
|
||||||
|
if (segement_child.id == 0x1F43B675): # Cluster element ID = 0x1F43B675
|
||||||
|
|
||||||
|
for cluster_child in segement_child:
|
||||||
|
if (cluster_child.id == 0xA3): # SimpleBlock element ID = xA3
|
||||||
|
simple_block_elements.append(cluster_child)
|
||||||
|
|
||||||
|
return simple_block_elements
|
||||||
|
|
||||||
|
def stop_thread(self):
|
||||||
|
self._stop_get_media = True
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# Read and parse streaming media from a Kinesis Video Stream
|
||||||
|
def run(self):
|
||||||
|
'''
|
||||||
|
Reads in chunks (unframed number of raw bytes) from a KVS GetMedia or GetMediaForFragmentList Streaming Body response
|
||||||
|
and parses into bounded MKV fragments. Raw data is buffered until a complete fragment is received which is then forwarded to the
|
||||||
|
on_fragmemt_arrived callback. Fragment is delivered as a raw byte array and also a parsed EBMLite Document that is a DOM like
|
||||||
|
structure of the elements (including Tags) within the given Fragment.
|
||||||
|
|
||||||
|
Kinesis Video will continually update the streaming buffer with media as soon as its available. For StartSelectorType = NOW,
|
||||||
|
bytes from the media stream will be available as fast as they arrive into Kinesis Video by the producer. In this case the
|
||||||
|
consumer bandwidth and fragment rate will be equal to that of the producer. However, if StartSelector is set to sometime
|
||||||
|
in the past then all fragments from start to end time will be available immediately. The effect is this will
|
||||||
|
read in bytes as fast as the system resources (KVS limits, CPU and bandwidth) will allow until the stream has
|
||||||
|
caught up with the leading edge of media being generated.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get the steam botocore.response.Streamingody object from the provided GetMedia response
|
||||||
|
kvs_streaming_buffer=self.get_media_response_object['Payload']
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
# Iterate through reading and parsing streaming body response of KVS GET Media API call to MKV fragments.
|
||||||
|
#########################################
|
||||||
|
chunk_buffer = bytearray()
|
||||||
|
fragment_read_start_time = timeit.default_timer()
|
||||||
|
|
||||||
|
chunk_read_count = 0
|
||||||
|
|
||||||
|
# Uses the StreamingBody object iterator to read in (default 1024 byte) chunks from the streaming buffer.
|
||||||
|
for chunk in kvs_streaming_buffer:
|
||||||
|
|
||||||
|
if self._stop_get_media:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Append chunk bytes to ByteArray buffer while waiting for the entire MKV fragment to arrive.
|
||||||
|
chunk_buffer.extend(chunk)
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Parse current byte buffer to MKV EBML DOM like object using EBMLite
|
||||||
|
#############################################
|
||||||
|
fragement_intrum_dom = self.schema.loads(chunk_buffer)
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Process a complete fragment if its arrived and send to the on_fragment_arrived callback.
|
||||||
|
#############################################
|
||||||
|
# EBML header elements indicate the start of a new fragment. Here we check if the start of a second fragment
|
||||||
|
# has arrived and use its start to identify the byte boundary of the first complete fragment to process.
|
||||||
|
ebml_header_elements = self._get_ebml_header_elements(fragement_intrum_dom)
|
||||||
|
|
||||||
|
# If multiple fragment headers then the first fragment has been received completely and ready to process.
|
||||||
|
if (len(ebml_header_elements) > 1):
|
||||||
|
|
||||||
|
# Get the offset for the first and second fragments. First fragment offset should be zero or fragment boundary is out of sync!
|
||||||
|
first_ebml_header_offset = ebml_header_elements[0].offset
|
||||||
|
second_ebml_header_offset = ebml_header_elements[1].offset
|
||||||
|
|
||||||
|
# Isolate the bytes from the first complete MKV fragments in the received chunk data
|
||||||
|
fragment_bytes = chunk_buffer[first_ebml_header_offset : second_ebml_header_offset]
|
||||||
|
|
||||||
|
# Parse the complete fragment as EBML to a DOM like object
|
||||||
|
fragment_dom = self.schema.loads(fragment_bytes)
|
||||||
|
|
||||||
|
# Calculate duration taken receiving this fragment - just for telemetry of the steaming data.
|
||||||
|
fragment_receive_duration = timeit.default_timer() - fragment_read_start_time
|
||||||
|
|
||||||
|
# Forward fragment to the on_fragment_arrived callback.
|
||||||
|
self.on_fragment_arrived_callback(self.stream_name,
|
||||||
|
fragment_bytes,
|
||||||
|
fragment_dom,
|
||||||
|
fragment_receive_duration)
|
||||||
|
|
||||||
|
# Remove the processed MKV segment from the raw byte chunk_buffer
|
||||||
|
chunk_buffer = chunk_buffer[second_ebml_header_offset: ]
|
||||||
|
|
||||||
|
# Reset the chunk read count.
|
||||||
|
chunk_read_count = 0
|
||||||
|
|
||||||
|
# Reset the start time for the next segment iteration just to time fragment durations
|
||||||
|
fragment_read_start_time = timeit.default_timer()
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Increment to chunk read count for this fragment
|
||||||
|
chunk_read_count +=1
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Exit the thread if the stream has no more chunks.
|
||||||
|
#############################################
|
||||||
|
#call the on_stream_read_complete() callback and exit the thread.
|
||||||
|
self.on_read_stream_complete_callback(self.stream_name)
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
# Pass any exceptions to exception callback.
|
||||||
|
self.on_read_stream_exception(self.stream_name, err)
|
||||||
|
|
||||||
|
|
||||||
141
lambda/cctv-people-rekognition/lambda_function.py
Normal file
141
lambda/cctv-people-rekognition/lambda_function.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from kinesis_video_streams_parser import KvsConsumerLibrary
|
||||||
|
from kinesis_video_fragment_processor import KvsFragementProcessor
|
||||||
|
|
||||||
|
rekognition_client = boto3.client("rekognition")
|
||||||
|
kvs_client = boto3.client('kinesisvideo')
|
||||||
|
kvs_fragment_processor = KvsFragementProcessor()
|
||||||
|
last_good_fragment_tags = None
|
||||||
|
|
||||||
|
stream_name = "" ## Provide KVS name here (stream must already exist)
|
||||||
|
stream_arn = "" ## Provide KVS ARN here (stream must already exist)
|
||||||
|
stream_processor_name = "" ## Provide a name for the stream processor
|
||||||
|
s3_bucket_name = "" ## Provide the S3 bucket
|
||||||
|
role_arn = "" ## Provide the role ARN for Rekognition
|
||||||
|
sns_topic_arn = "" ## Provide the SNS ARN
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
## KVS Consumer Library Callbacks
|
||||||
|
|
||||||
|
def on_fragment_arrived(stream_name, fragment_bytes, fragment_dom, fragment_receive_duration):
|
||||||
|
try:
|
||||||
|
last_good_fragment_tags = kvs_fragment_processor.get_fragment_tags(fragment_dom)
|
||||||
|
fragment_num = last_good_fragment_tags['AWS_KINESISVIDEO_FRAGMENT_NUMBER']
|
||||||
|
rekognition_client.start_stream_processor(
|
||||||
|
Name=stream_processor_name,
|
||||||
|
StartSelector={
|
||||||
|
'KVSStreamStartSelector': {
|
||||||
|
'FragmentNumber': fragment_num,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StopSelector={
|
||||||
|
'MaxDurationInSeconds': 2
|
||||||
|
}
|
||||||
|
)
|
||||||
|
time.sleep(2)
|
||||||
|
except Exception as err:
|
||||||
|
print(err)
|
||||||
|
|
||||||
|
def on_stream_read_complete(stream_name):
|
||||||
|
print(f"Stream {stream_name} read complete")
|
||||||
|
|
||||||
|
def on_stream_read_exception(stream_name, error):
|
||||||
|
print(f"Stream {stream_name} read exception: {error}")
|
||||||
|
|
||||||
|
## Main Lambda Function
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
## Step 1: Ensure stream processor is deleted
|
||||||
|
try:
|
||||||
|
rekognition_client.delete_stream_processor(
|
||||||
|
Name=stream_processor_name,
|
||||||
|
)
|
||||||
|
rekognition_client.create_stream_processor(
|
||||||
|
Input={
|
||||||
|
'KinesisVideoStream': {
|
||||||
|
'Arn': stream_arn
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Output={
|
||||||
|
'S3Destination': {
|
||||||
|
'Bucket': s3_bucket_name,
|
||||||
|
'KeyPrefix': 'stream-results'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Name=stream_processor_name,
|
||||||
|
Settings={
|
||||||
|
'ConnectedHome': {
|
||||||
|
'Labels': [
|
||||||
|
'PERSON',
|
||||||
|
],
|
||||||
|
'MinConfidence': 80
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RoleArn=role_arn,
|
||||||
|
NotificationChannel={
|
||||||
|
'SNSTopicArn':sns_topic_arn
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
## Step 2: Create Rekognition Stream Processor
|
||||||
|
rekognition_client.create_stream_processor(
|
||||||
|
Input={
|
||||||
|
'KinesisVideoStream': {
|
||||||
|
'Arn': stream_arn
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Output={
|
||||||
|
'S3Destination': {
|
||||||
|
'Bucket': s3_bucket_name,
|
||||||
|
'KeyPrefix': 'stream-results'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Name=stream_processor_name,
|
||||||
|
Settings={
|
||||||
|
'ConnectedHome': {
|
||||||
|
'Labels': [
|
||||||
|
'PERSON',
|
||||||
|
],
|
||||||
|
'MinConfidence': 80
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RoleArn=role_arn,
|
||||||
|
NotificationChannel={
|
||||||
|
'SNSTopicArn':sns_topic_arn
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
## Step 3: Prepare connection to stream
|
||||||
|
kvs_client = boto3.client('kinesisvideo')
|
||||||
|
|
||||||
|
response = kvs_client.get_data_endpoint(
|
||||||
|
StreamARN=stream_arn,
|
||||||
|
APIName='GET_MEDIA'
|
||||||
|
)
|
||||||
|
|
||||||
|
get_endpoint = response['DataEndpoint']
|
||||||
|
|
||||||
|
kvs_media_client = boto3.client('kinesis-video-media', endpoint_url=get_endpoint)
|
||||||
|
get_media_response = kvs_media_client.get_media(
|
||||||
|
StreamName=stream_name,
|
||||||
|
StartSelector={
|
||||||
|
'StartSelectorType': 'NOW'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
## Step 4: Prepare consumer library
|
||||||
|
my_stream01_consumer = KvsConsumerLibrary(stream_name,
|
||||||
|
get_media_response,
|
||||||
|
on_fragment_arrived,
|
||||||
|
on_stream_read_complete,
|
||||||
|
on_stream_read_exception
|
||||||
|
)
|
||||||
|
|
||||||
|
## Step 5: Run consumer
|
||||||
|
my_stream01_consumer.run()
|
||||||
160
lambda/cctv-people-sns-dynamodb/lambda_function.py
Normal file
160
lambda/cctv-people-sns-dynamodb/lambda_function.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import boto3
|
||||||
|
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
now = datetime.utcnow()
|
||||||
|
dynamodb = boto3.resource("dynamodb")
|
||||||
|
table = dynamodb.Table("cctv-people-analytics-table")
|
||||||
|
region = "eu-west-1"
|
||||||
|
|
||||||
|
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
|
||||||
|
from requests_aws4auth import AWS4Auth
|
||||||
|
|
||||||
|
service = 'aoss'
|
||||||
|
credentials = boto3.Session().get_credentials().get_frozen_credentials()
|
||||||
|
print(credentials.access_key, credentials.secret_key, credentials.token)
|
||||||
|
|
||||||
|
auth = AWSV4SignerAuth(credentials, region, service)
|
||||||
|
|
||||||
|
auth = AWS4Auth(
|
||||||
|
credentials.access_key,
|
||||||
|
credentials.secret_key,
|
||||||
|
region,
|
||||||
|
service,
|
||||||
|
session_token=credentials.token # ✅ include session token!
|
||||||
|
)
|
||||||
|
|
||||||
|
host = "5xmv01a4621tqq8agz99.eu-west-1.aoss.amazonaws.com"
|
||||||
|
index = now.strftime("cctv-people-analytics-%Y-%m-%d-%H")
|
||||||
|
url = f"{host}/{index}/_doc"
|
||||||
|
|
||||||
|
client = OpenSearch(
|
||||||
|
hosts=[{'host': host, 'port': 443}],
|
||||||
|
http_auth=auth,
|
||||||
|
use_ssl=True,
|
||||||
|
verify_certs=True,
|
||||||
|
connection_class=RequestsHttpConnection,
|
||||||
|
pool_maxsize=20,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not client.indices.exists(index=index):
|
||||||
|
client.indices.create(index=index)
|
||||||
|
|
||||||
|
def normalize_timestamp(ts: str) -> str:
|
||||||
|
"""
|
||||||
|
Normalize timestamp into ISO 8601 with UTC (Z).
|
||||||
|
Handles:
|
||||||
|
- %Y-%m-%dT%H-%M-%S (bad format with dashes)
|
||||||
|
- %Y-%m-%dT%H:%M:%S (already correct)
|
||||||
|
- Epoch numbers (int/float)
|
||||||
|
"""
|
||||||
|
if isinstance(ts, (int, float)): # epoch
|
||||||
|
return datetime.fromtimestamp(ts/1000, tz=timezone.utc).isoformat().replace("+00:00", "Z")
|
||||||
|
|
||||||
|
if isinstance(ts, str):
|
||||||
|
# Already ISO with colons
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S")
|
||||||
|
return dt.replace(tzinfo=timezone.utc).isoformat().replace("+00:00", "Z")
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Bad format with dashes
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(ts, "%Y-%m-%dT%H-%M-%S")
|
||||||
|
return dt.replace(tzinfo=timezone.utc).isoformat().replace("+00:00", "Z")
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback: return as-is
|
||||||
|
return str(ts)
|
||||||
|
|
||||||
|
def epoch_converter(ts):
|
||||||
|
"""
|
||||||
|
Normalize timestamp into epoch milliseconds (integer).
|
||||||
|
"""
|
||||||
|
if isinstance(ts, (int, float)):
|
||||||
|
# already epoch
|
||||||
|
return int(ts)
|
||||||
|
if isinstance(ts, str):
|
||||||
|
try:
|
||||||
|
# Try ISO format
|
||||||
|
dt = datetime.strptime(ts, "%Y-%m-%dT%H:%M:%S")
|
||||||
|
except ValueError:
|
||||||
|
# Bad format with dashes
|
||||||
|
dt = datetime.strptime(ts, "%Y-%m-%dT%H-%M-%S")
|
||||||
|
# Convert to UTC epoch milliseconds
|
||||||
|
dt = dt.replace(tzinfo=timezone.utc)
|
||||||
|
return int(dt.timestamp() * 1000)
|
||||||
|
return int(ts) # fallback
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
print(event)
|
||||||
|
dict_stream_camid = {
|
||||||
|
'CCTV-Stream': 'iMbYu7fVoHRP',
|
||||||
|
'TAPO-C246D-Stream': 'piYD7HWDKAub',
|
||||||
|
'TAPO-C500-Stream': 'EZ4DnbJjqFvk',
|
||||||
|
'HIKVision-2MP-Dome-1-Stream': 'x4j8eVyrF6mO',
|
||||||
|
'HIKVision-2MP-Dome-2-Stream': 'NnIXfhJeqX6i',
|
||||||
|
'HIKVision-2MP-Bullet-1-Stream': 'vP2wVuCEN5vi',
|
||||||
|
'HIKVision-2MP-Bullet-2-Stream': 'SnPNqUVLHinu',
|
||||||
|
'Dahua-2MP-Dome-1-Stream': 'YMpY5kku8IXg',
|
||||||
|
'Dahua-2MP-Dome-2-Stream': 'yyzi5SqLTftn',
|
||||||
|
'Dahua-2MP-Bullet-1-Stream': 'YTOYlpGjviLB',
|
||||||
|
'Dahua-2MP-Bullet-2-Stream': 'cLVBa3vAV4yf'
|
||||||
|
}
|
||||||
|
|
||||||
|
notification = json.loads(event['Records'][0]['Sns']['Message'])
|
||||||
|
if notification['eventNamespace']['type'] == 'LABEL_DETECTED':
|
||||||
|
stream_arn = notification['inputInformation']['kinesisVideo']['streamArn']
|
||||||
|
camera_id = dict_stream_camid[stream_arn.split('/')[1]]
|
||||||
|
|
||||||
|
for label in notification['labels']:
|
||||||
|
label_id = label['id']
|
||||||
|
detect_timestamp = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
|
||||||
|
frame_timestamp = label['videoMapping']['kinesisVideoMapping']['serverTimestamp']
|
||||||
|
frame_url = str.replace(label['frameImageUri'], 's3://cctv-stream-results/', 'https://cctv-stream-results.s3.eu-west-1.amazonaws.com/')
|
||||||
|
label_name = label['name']
|
||||||
|
confidence = label['confidence']
|
||||||
|
|
||||||
|
to_upload_dynamodb = {
|
||||||
|
'camera_id': camera_id,
|
||||||
|
'label_id': label_id,
|
||||||
|
'detect_timestamp': normalize_timestamp(detect_timestamp),
|
||||||
|
'frame_timestamp': normalize_timestamp(frame_timestamp),
|
||||||
|
'frame_url': frame_url,
|
||||||
|
'label_name': label_name,
|
||||||
|
'confidence': Decimal(str(confidence)),
|
||||||
|
'rekognition_response': json.dumps(label)
|
||||||
|
}
|
||||||
|
print()
|
||||||
|
response= table.put_item(Item=to_upload_dynamodb)
|
||||||
|
if response['ResponseMetadata']['HTTPStatusCode'] == 200:
|
||||||
|
print("Item successfully written!")
|
||||||
|
|
||||||
|
# headers = { "Content-Type": "application/json" }
|
||||||
|
# response = requests.post(url, auth=awsauth, json=to_upload, headers=headers)
|
||||||
|
# print(response.text, response.status_code)
|
||||||
|
to_upload_es = {
|
||||||
|
'label_id': label_id,
|
||||||
|
'@timestamp': normalize_timestamp(detect_timestamp),
|
||||||
|
'frame_timestamp': normalize_timestamp(frame_timestamp),
|
||||||
|
'frame_url': frame_url,
|
||||||
|
'label_name': label_name,
|
||||||
|
'confidence': Decimal(str(confidence))
|
||||||
|
}
|
||||||
|
|
||||||
|
####
|
||||||
|
response = client.index(
|
||||||
|
index = index,
|
||||||
|
body = to_upload_es
|
||||||
|
)
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'statusCode': 200,
|
||||||
|
'body': json.dumps('Processing cctv results success'),
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user