Initial working version
This commit is contained in:
+319
@@ -0,0 +1,319 @@
|
||||
/// <reference types="node" />
|
||||
import { IPicture, ITag } from '../type';
|
||||
import GUID from './GUID';
|
||||
import { IGetToken, ITokenizer } from 'strtok3/lib/core';
|
||||
/**
|
||||
* Data Type: Specifies the type of information being stored. The following values are recognized.
|
||||
*/
|
||||
export declare enum DataType {
|
||||
/**
|
||||
* Unicode string. The data consists of a sequence of Unicode characters.
|
||||
*/
|
||||
UnicodeString = 0,
|
||||
/**
|
||||
* BYTE array. The type of data is implementation-specific.
|
||||
*/
|
||||
ByteArray = 1,
|
||||
/**
|
||||
* BOOL. The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values.
|
||||
*/
|
||||
Bool = 2,
|
||||
/**
|
||||
* DWORD. The data is 4 bytes long and should be interpreted as a 32-bit unsigned integer.
|
||||
*/
|
||||
DWord = 3,
|
||||
/**
|
||||
* QWORD. The data is 8 bytes long and should be interpreted as a 64-bit unsigned integer.
|
||||
*/
|
||||
QWord = 4,
|
||||
/**
|
||||
* WORD. The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer.
|
||||
*/
|
||||
Word = 5
|
||||
}
|
||||
/**
|
||||
* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ee663575
|
||||
*/
|
||||
export interface IAsfObjectHeader {
|
||||
/**
|
||||
* A GUID that identifies the object. 128 bits
|
||||
*/
|
||||
objectId: GUID;
|
||||
/**
|
||||
* The size of the object (64-bits)
|
||||
*/
|
||||
objectSize: number;
|
||||
}
|
||||
/**
|
||||
* Interface for: 3. ASF top-level Header Object
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3
|
||||
*/
|
||||
export interface IAsfTopLevelObjectHeader extends IAsfObjectHeader {
|
||||
numberOfHeaderObjects: number;
|
||||
}
|
||||
/**
|
||||
* Token for: 3. ASF top-level Header Object
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3
|
||||
*/
|
||||
export declare const TopLevelHeaderObjectToken: IGetToken<IAsfTopLevelObjectHeader, Buffer>;
|
||||
/**
|
||||
* Token for: 3.1 Header Object (mandatory, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_1
|
||||
*/
|
||||
export declare const HeaderObjectToken: IGetToken<IAsfObjectHeader, Buffer>;
|
||||
export declare abstract class State<T> implements IGetToken<T> {
|
||||
len: number;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
abstract get(buf: Buffer, off: number): T;
|
||||
protected postProcessTag(tags: ITag[], name: string, valueType: number, data: any): void;
|
||||
}
|
||||
export declare class IgnoreObjectState extends State<any> {
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): null;
|
||||
}
|
||||
/**
|
||||
* Interface for: 3.2: File Properties Object (mandatory, one only)
|
||||
*
|
||||
* The File Properties Object defines the global characteristics of the combined digital media streams found within the Data Object.
|
||||
*/
|
||||
export interface IFilePropertiesObject {
|
||||
/**
|
||||
* Specifies the unique identifier for this file.
|
||||
* The value of this field shall be regenerated every time the file is modified in any way.
|
||||
* The value of this field shall be identical to the value of the File ID field of the Data Object.
|
||||
*/
|
||||
fileId: GUID;
|
||||
/**
|
||||
* Specifies the size, in bytes, of the entire file.
|
||||
* The value of this field is invalid if the Broadcast Flag bit in the Flags field is set to 1.
|
||||
*/
|
||||
fileSize: bigint;
|
||||
/**
|
||||
* Specifies the date and time of the initial creation of the file. The value is given as the number of 100-nanosecond
|
||||
* intervals since January 1, 1601, according to Coordinated Universal Time (Greenwich Mean Time). The value of this
|
||||
* field may be invalid if the Broadcast Flag bit in the Flags field is set to 1.
|
||||
*/
|
||||
creationDate: bigint;
|
||||
/**
|
||||
* Specifies the number of Data Packet entries that exist within the Data Object. The value of this field is invalid
|
||||
* if the Broadcast Flag bit in the Flags field is set to 1.
|
||||
*/
|
||||
dataPacketsCount: bigint;
|
||||
/**
|
||||
* Specifies the time needed to play the file in 100-nanosecond units.
|
||||
* This value should include the duration (estimated, if an exact value is unavailable) of the the last media object
|
||||
* in the presentation. The value of this field is invalid if the Broadcast Flag bit in the Flags field is set to 1.
|
||||
*/
|
||||
playDuration: bigint;
|
||||
/**
|
||||
* Specifies the time needed to send the file in 100-nanosecond units.
|
||||
* This value should include the duration of the last packet in the content.
|
||||
* The value of this field is invalid if the Broadcast Flag bit in the Flags field is set to 1.
|
||||
* Players can ignore this value.
|
||||
*/
|
||||
sendDuration: bigint;
|
||||
/**
|
||||
* Specifies the amount of time to buffer data before starting to play the file, in millisecond units.
|
||||
* If this value is nonzero, the Play Duration field and all of the payload Presentation Time fields have been offset
|
||||
* by this amount. Therefore, player software must subtract the value in the preroll field from the play duration and
|
||||
* presentation times to calculate their actual values. It follows that all payload Presentation Time fields need to
|
||||
* be at least this value.
|
||||
*/
|
||||
preroll: bigint;
|
||||
/**
|
||||
* The flags
|
||||
*/
|
||||
flags: {
|
||||
/**
|
||||
* Specifies, if set, that a file is in the process of being created (for example, for recording applications),
|
||||
* and thus that various values stored in the header objects are invalid. It is highly recommended that
|
||||
* post-processing be performed to remove this condition at the earliest opportunity.
|
||||
*/
|
||||
broadcast: boolean;
|
||||
/**
|
||||
* Specifies, if set, that a file is seekable.
|
||||
* Note that for files containing a single audio stream and a Minimum Data Packet Size field equal to the Maximum
|
||||
* Data Packet Size field, this flag shall always be set to 1.
|
||||
* For files containing a single audio stream and a video stream or mutually exclusive video streams,
|
||||
* this flag is only set to 1 if the file contains a matching Simple Index Object for each regular video stream
|
||||
* (that is, video streams that are not hidden according to the method described in section 8.2.2).
|
||||
*/
|
||||
seekable: boolean;
|
||||
};
|
||||
/**
|
||||
* Specifies the minimum Data Packet size in bytes. In general, the value of this field is invalid if the Broadcast
|
||||
* Flag bit in the Flags field is set to 1.
|
||||
* However, for the purposes of this specification, the values for the Minimum Data Packet Size and Maximum Data
|
||||
* Packet Size fields shall be set to the same value, and this value should be set to the packet size, even when the
|
||||
* Broadcast Flag in the Flags field is set to 1.
|
||||
*/
|
||||
minimumDataPacketSize: number;
|
||||
/**
|
||||
* Specifies the maximum Data Packet size in bytes.
|
||||
* In general, the value of this field is invalid if the Broadcast Flag bit in the Flags field is set to 1.
|
||||
* However, for the purposes of this specification, the values of the Minimum Data Packet Size and Maximum Data Packet
|
||||
* Size fields shall be set to the same value,
|
||||
* and this value should be set to the packet size, even when the Broadcast Flag field is set to 1.
|
||||
*/
|
||||
maximumDataPacketSize: number;
|
||||
/**
|
||||
* Specifies the maximum instantaneous bit rate in bits per second for the entire file.
|
||||
* This shall equal the sum of the bit rates of the individual digital media streams.
|
||||
* It shall be noted that the digital media stream includes ASF data packetization overhead as well as digital media
|
||||
* data in payloads.
|
||||
* Only those streams that have a free-standing Stream Properties Object in the header shall have their bit rates
|
||||
* included in the sum;
|
||||
* streams whose Stream Properties Object exists as part of an Extended Stream Properties Object in the Header
|
||||
* Extension Object shall not have their bit rates included in this sum, except when this value would otherwise be 0.
|
||||
*/
|
||||
maximumBitrate: number;
|
||||
}
|
||||
/**
|
||||
* Token for: 3.2: File Properties Object (mandatory, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_2
|
||||
*/
|
||||
export declare class FilePropertiesObject extends State<IFilePropertiesObject> {
|
||||
static guid: GUID;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): IFilePropertiesObject;
|
||||
}
|
||||
/**
|
||||
* Interface for: 3.3 Stream Properties Object (mandatory, one per stream)
|
||||
*/
|
||||
export interface IStreamPropertiesObject {
|
||||
/**
|
||||
* Stream Type
|
||||
*/
|
||||
streamType: string;
|
||||
/**
|
||||
* Error Correction Type
|
||||
*/
|
||||
errorCorrectionType: GUID;
|
||||
}
|
||||
/**
|
||||
* Token for: 3.3 Stream Properties Object (mandatory, one per stream)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_3
|
||||
*/
|
||||
export declare class StreamPropertiesObject extends State<IStreamPropertiesObject> {
|
||||
static guid: GUID;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): IStreamPropertiesObject;
|
||||
}
|
||||
export interface IHeaderExtensionObject {
|
||||
reserved1: GUID;
|
||||
reserved2: number;
|
||||
extensionDataSize: number;
|
||||
}
|
||||
/**
|
||||
* 3.4: Header Extension Object (mandatory, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_4
|
||||
*/
|
||||
export declare class HeaderExtensionObject implements IGetToken<IHeaderExtensionObject> {
|
||||
static guid: GUID;
|
||||
len: number;
|
||||
constructor();
|
||||
get(buf: Buffer, off: number): IHeaderExtensionObject;
|
||||
}
|
||||
export interface ICodecEntry {
|
||||
type: {
|
||||
videoCodec: boolean;
|
||||
audioCodec: boolean;
|
||||
};
|
||||
codecName: string;
|
||||
description: string;
|
||||
information: Buffer;
|
||||
}
|
||||
/**
|
||||
* 3.5: Read the Codec-List-Object, which provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_5
|
||||
*/
|
||||
export declare function readCodecEntries(tokenizer: ITokenizer): Promise<ICodecEntry[]>;
|
||||
/**
|
||||
* 3.10 Content Description Object (optional, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_10
|
||||
*/
|
||||
export declare class ContentDescriptionObjectState extends State<ITag[]> {
|
||||
static guid: GUID;
|
||||
private static contentDescTags;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): ITag[];
|
||||
}
|
||||
/**
|
||||
* 3.11 Extended Content Description Object (optional, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_11
|
||||
*/
|
||||
export declare class ExtendedContentDescriptionObjectState extends State<ITag[]> {
|
||||
static guid: GUID;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): ITag[];
|
||||
}
|
||||
export interface IStreamName {
|
||||
streamLanguageId: number;
|
||||
streamName: string;
|
||||
}
|
||||
/**
|
||||
* 4.1 Extended Stream Properties Object (optional, 1 per media stream)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/04_objects_in_the_asf_header_extension_object.html#4_1
|
||||
*/
|
||||
export interface IExtendedStreamPropertiesObject {
|
||||
startTime: bigint;
|
||||
endTime: bigint;
|
||||
dataBitrate: number;
|
||||
bufferSize: number;
|
||||
initialBufferFullness: number;
|
||||
alternateDataBitrate: number;
|
||||
alternateBufferSize: number;
|
||||
alternateInitialBufferFullness: number;
|
||||
maximumObjectSize: number;
|
||||
flags: {
|
||||
reliableFlag: boolean;
|
||||
seekableFlag: boolean;
|
||||
resendLiveCleanpointsFlag: boolean;
|
||||
};
|
||||
streamNumber: number;
|
||||
streamLanguageId: number;
|
||||
averageTimePerFrame: number;
|
||||
streamNameCount: number;
|
||||
payloadExtensionSystems: number;
|
||||
streamNames: IStreamName[];
|
||||
streamPropertiesObject: number;
|
||||
}
|
||||
/**
|
||||
* 4.1 Extended Stream Properties Object (optional, 1 per media stream)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/04_objects_in_the_asf_header_extension_object.html#4_1
|
||||
*/
|
||||
export declare class ExtendedStreamPropertiesObjectState extends State<IExtendedStreamPropertiesObject> {
|
||||
static guid: GUID;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): IExtendedStreamPropertiesObject;
|
||||
}
|
||||
/**
|
||||
* 4.7 Metadata Object (optional, 0 or 1)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/04_objects_in_the_asf_header_extension_object.html#4_7
|
||||
*/
|
||||
export declare class MetadataObjectState extends State<ITag[]> {
|
||||
static guid: GUID;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
get(buf: Buffer, off: number): ITag[];
|
||||
}
|
||||
export declare class MetadataLibraryObjectState extends MetadataObjectState {
|
||||
static guid: GUID;
|
||||
constructor(header: IAsfObjectHeader);
|
||||
}
|
||||
export interface IWmPicture extends IPicture {
|
||||
type: string;
|
||||
format: string;
|
||||
description: string;
|
||||
size: number;
|
||||
data: Buffer;
|
||||
}
|
||||
/**
|
||||
* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd757977(v=vs.85).aspx
|
||||
*/
|
||||
export declare class WmPictureToken implements IGetToken<IWmPicture> {
|
||||
len: any;
|
||||
static fromBase64(base64str: string): IPicture;
|
||||
static fromBuffer(buffer: Buffer): IWmPicture;
|
||||
constructor(len: any);
|
||||
get(buffer: Buffer, offset: number): IWmPicture;
|
||||
}
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
"use strict";
|
||||
// ASF Objects
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WmPictureToken = exports.MetadataLibraryObjectState = exports.MetadataObjectState = exports.ExtendedStreamPropertiesObjectState = exports.ExtendedContentDescriptionObjectState = exports.ContentDescriptionObjectState = exports.readCodecEntries = exports.HeaderExtensionObject = exports.StreamPropertiesObject = exports.FilePropertiesObject = exports.IgnoreObjectState = exports.State = exports.HeaderObjectToken = exports.TopLevelHeaderObjectToken = exports.DataType = void 0;
|
||||
const util = require("../common/Util");
|
||||
const Token = require("token-types");
|
||||
const GUID_1 = require("./GUID");
|
||||
const AsfUtil_1 = require("./AsfUtil");
|
||||
const ID3v2Token_1 = require("../id3v2/ID3v2Token");
|
||||
/**
|
||||
* Data Type: Specifies the type of information being stored. The following values are recognized.
|
||||
*/
|
||||
var DataType;
|
||||
(function (DataType) {
|
||||
/**
|
||||
* Unicode string. The data consists of a sequence of Unicode characters.
|
||||
*/
|
||||
DataType[DataType["UnicodeString"] = 0] = "UnicodeString";
|
||||
/**
|
||||
* BYTE array. The type of data is implementation-specific.
|
||||
*/
|
||||
DataType[DataType["ByteArray"] = 1] = "ByteArray";
|
||||
/**
|
||||
* BOOL. The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values.
|
||||
*/
|
||||
DataType[DataType["Bool"] = 2] = "Bool";
|
||||
/**
|
||||
* DWORD. The data is 4 bytes long and should be interpreted as a 32-bit unsigned integer.
|
||||
*/
|
||||
DataType[DataType["DWord"] = 3] = "DWord";
|
||||
/**
|
||||
* QWORD. The data is 8 bytes long and should be interpreted as a 64-bit unsigned integer.
|
||||
*/
|
||||
DataType[DataType["QWord"] = 4] = "QWord";
|
||||
/**
|
||||
* WORD. The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer.
|
||||
*/
|
||||
DataType[DataType["Word"] = 5] = "Word";
|
||||
})(DataType = exports.DataType || (exports.DataType = {}));
|
||||
/**
|
||||
* Token for: 3. ASF top-level Header Object
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3
|
||||
*/
|
||||
exports.TopLevelHeaderObjectToken = {
|
||||
len: 30,
|
||||
get: (buf, off) => {
|
||||
return {
|
||||
objectId: GUID_1.default.fromBin(new Token.BufferType(16).get(buf, off)),
|
||||
objectSize: Number(Token.UINT64_LE.get(buf, off + 16)),
|
||||
numberOfHeaderObjects: Token.UINT32_LE.get(buf, off + 24)
|
||||
// Reserved: 2 bytes
|
||||
};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Token for: 3.1 Header Object (mandatory, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_1
|
||||
*/
|
||||
exports.HeaderObjectToken = {
|
||||
len: 24,
|
||||
get: (buf, off) => {
|
||||
return {
|
||||
objectId: GUID_1.default.fromBin(new Token.BufferType(16).get(buf, off)),
|
||||
objectSize: Number(Token.UINT64_LE.get(buf, off + 16))
|
||||
};
|
||||
}
|
||||
};
|
||||
class State {
|
||||
constructor(header) {
|
||||
this.len = Number(header.objectSize) - exports.HeaderObjectToken.len;
|
||||
}
|
||||
postProcessTag(tags, name, valueType, data) {
|
||||
if (name === 'WM/Picture') {
|
||||
tags.push({ id: name, value: WmPictureToken.fromBuffer(data) });
|
||||
}
|
||||
else {
|
||||
const parseAttr = AsfUtil_1.AsfUtil.getParserForAttr(valueType);
|
||||
if (!parseAttr) {
|
||||
throw new Error('unexpected value headerType: ' + valueType);
|
||||
}
|
||||
tags.push({ id: name, value: parseAttr(data) });
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.State = State;
|
||||
// ToDo: use ignore type
|
||||
class IgnoreObjectState extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.IgnoreObjectState = IgnoreObjectState;
|
||||
/**
|
||||
* Token for: 3.2: File Properties Object (mandatory, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_2
|
||||
*/
|
||||
class FilePropertiesObject extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
return {
|
||||
fileId: GUID_1.default.fromBin(buf, off),
|
||||
fileSize: Token.UINT64_LE.get(buf, off + 16),
|
||||
creationDate: Token.UINT64_LE.get(buf, off + 24),
|
||||
dataPacketsCount: Token.UINT64_LE.get(buf, off + 32),
|
||||
playDuration: Token.UINT64_LE.get(buf, off + 40),
|
||||
sendDuration: Token.UINT64_LE.get(buf, off + 48),
|
||||
preroll: Token.UINT64_LE.get(buf, off + 56),
|
||||
flags: {
|
||||
broadcast: util.getBit(buf, off + 64, 24),
|
||||
seekable: util.getBit(buf, off + 64, 25)
|
||||
},
|
||||
// flagsNumeric: Token.UINT32_LE.get(buf, off + 64),
|
||||
minimumDataPacketSize: Token.UINT32_LE.get(buf, off + 68),
|
||||
maximumDataPacketSize: Token.UINT32_LE.get(buf, off + 72),
|
||||
maximumBitrate: Token.UINT32_LE.get(buf, off + 76)
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.FilePropertiesObject = FilePropertiesObject;
|
||||
FilePropertiesObject.guid = GUID_1.default.FilePropertiesObject;
|
||||
/**
|
||||
* Token for: 3.3 Stream Properties Object (mandatory, one per stream)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_3
|
||||
*/
|
||||
class StreamPropertiesObject extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
return {
|
||||
streamType: GUID_1.default.decodeMediaType(GUID_1.default.fromBin(buf, off)),
|
||||
errorCorrectionType: GUID_1.default.fromBin(buf, off + 8)
|
||||
// ToDo
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.StreamPropertiesObject = StreamPropertiesObject;
|
||||
StreamPropertiesObject.guid = GUID_1.default.StreamPropertiesObject;
|
||||
/**
|
||||
* 3.4: Header Extension Object (mandatory, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_4
|
||||
*/
|
||||
class HeaderExtensionObject {
|
||||
constructor() {
|
||||
this.len = 22;
|
||||
}
|
||||
get(buf, off) {
|
||||
return {
|
||||
reserved1: GUID_1.default.fromBin(buf, off),
|
||||
reserved2: buf.readUInt16LE(off + 16),
|
||||
extensionDataSize: buf.readUInt32LE(off + 18)
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.HeaderExtensionObject = HeaderExtensionObject;
|
||||
HeaderExtensionObject.guid = GUID_1.default.HeaderExtensionObject;
|
||||
/**
|
||||
* 3.5: The Codec List Object provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_5
|
||||
*/
|
||||
const CodecListObjectHeader = {
|
||||
len: 20,
|
||||
get: (buf, off) => {
|
||||
return {
|
||||
entryCount: buf.readUInt16LE(off + 16)
|
||||
};
|
||||
}
|
||||
};
|
||||
async function readString(tokenizer) {
|
||||
const length = await tokenizer.readNumber(Token.UINT16_LE);
|
||||
return (await tokenizer.readToken(new Token.StringType(length * 2, 'utf16le'))).replace('\0', '');
|
||||
}
|
||||
/**
|
||||
* 3.5: Read the Codec-List-Object, which provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_5
|
||||
*/
|
||||
async function readCodecEntries(tokenizer) {
|
||||
const codecHeader = await tokenizer.readToken(CodecListObjectHeader);
|
||||
const entries = [];
|
||||
for (let i = 0; i < codecHeader.entryCount; ++i) {
|
||||
entries.push(await readCodecEntry(tokenizer));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
exports.readCodecEntries = readCodecEntries;
|
||||
async function readInformation(tokenizer) {
|
||||
const length = await tokenizer.readNumber(Token.UINT16_LE);
|
||||
const buf = Buffer.alloc(length);
|
||||
await tokenizer.readBuffer(buf);
|
||||
return buf;
|
||||
}
|
||||
/**
|
||||
* Read Codec-Entries
|
||||
* @param tokenizer
|
||||
*/
|
||||
async function readCodecEntry(tokenizer) {
|
||||
const type = await tokenizer.readNumber(Token.UINT16_LE);
|
||||
return {
|
||||
type: {
|
||||
videoCodec: (type & 0x0001) === 0x0001,
|
||||
audioCodec: (type & 0x0002) === 0x0002
|
||||
},
|
||||
codecName: await readString(tokenizer),
|
||||
description: await readString(tokenizer),
|
||||
information: await readInformation(tokenizer)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 3.10 Content Description Object (optional, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_10
|
||||
*/
|
||||
class ContentDescriptionObjectState extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
const tags = [];
|
||||
let pos = off + 10;
|
||||
for (let i = 0; i < ContentDescriptionObjectState.contentDescTags.length; ++i) {
|
||||
const length = buf.readUInt16LE(off + i * 2);
|
||||
if (length > 0) {
|
||||
const tagName = ContentDescriptionObjectState.contentDescTags[i];
|
||||
const end = pos + length;
|
||||
tags.push({ id: tagName, value: AsfUtil_1.AsfUtil.parseUnicodeAttr(buf.slice(pos, end)) });
|
||||
pos = end;
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
exports.ContentDescriptionObjectState = ContentDescriptionObjectState;
|
||||
ContentDescriptionObjectState.guid = GUID_1.default.ContentDescriptionObject;
|
||||
ContentDescriptionObjectState.contentDescTags = ['Title', 'Author', 'Copyright', 'Description', 'Rating'];
|
||||
/**
|
||||
* 3.11 Extended Content Description Object (optional, one only)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_11
|
||||
*/
|
||||
class ExtendedContentDescriptionObjectState extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
const tags = [];
|
||||
const attrCount = buf.readUInt16LE(off);
|
||||
let pos = off + 2;
|
||||
for (let i = 0; i < attrCount; i += 1) {
|
||||
const nameLen = buf.readUInt16LE(pos);
|
||||
pos += 2;
|
||||
const name = AsfUtil_1.AsfUtil.parseUnicodeAttr(buf.slice(pos, pos + nameLen));
|
||||
pos += nameLen;
|
||||
const valueType = buf.readUInt16LE(pos);
|
||||
pos += 2;
|
||||
const valueLen = buf.readUInt16LE(pos);
|
||||
pos += 2;
|
||||
const value = buf.slice(pos, pos + valueLen);
|
||||
pos += valueLen;
|
||||
this.postProcessTag(tags, name, valueType, value);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
exports.ExtendedContentDescriptionObjectState = ExtendedContentDescriptionObjectState;
|
||||
ExtendedContentDescriptionObjectState.guid = GUID_1.default.ExtendedContentDescriptionObject;
|
||||
/**
|
||||
* 4.1 Extended Stream Properties Object (optional, 1 per media stream)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/04_objects_in_the_asf_header_extension_object.html#4_1
|
||||
*/
|
||||
class ExtendedStreamPropertiesObjectState extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
return {
|
||||
startTime: Token.UINT64_LE.get(buf, off),
|
||||
endTime: Token.UINT64_LE.get(buf, off + 8),
|
||||
dataBitrate: buf.readInt32LE(off + 12),
|
||||
bufferSize: buf.readInt32LE(off + 16),
|
||||
initialBufferFullness: buf.readInt32LE(off + 20),
|
||||
alternateDataBitrate: buf.readInt32LE(off + 24),
|
||||
alternateBufferSize: buf.readInt32LE(off + 28),
|
||||
alternateInitialBufferFullness: buf.readInt32LE(off + 32),
|
||||
maximumObjectSize: buf.readInt32LE(off + 36),
|
||||
flags: {
|
||||
reliableFlag: util.getBit(buf, off + 40, 0),
|
||||
seekableFlag: util.getBit(buf, off + 40, 1),
|
||||
resendLiveCleanpointsFlag: util.getBit(buf, off + 40, 2)
|
||||
},
|
||||
// flagsNumeric: Token.UINT32_LE.get(buf, off + 64),
|
||||
streamNumber: buf.readInt16LE(off + 42),
|
||||
streamLanguageId: buf.readInt16LE(off + 44),
|
||||
averageTimePerFrame: buf.readInt32LE(off + 52),
|
||||
streamNameCount: buf.readInt32LE(off + 54),
|
||||
payloadExtensionSystems: buf.readInt32LE(off + 56),
|
||||
streamNames: [],
|
||||
streamPropertiesObject: null
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.ExtendedStreamPropertiesObjectState = ExtendedStreamPropertiesObjectState;
|
||||
ExtendedStreamPropertiesObjectState.guid = GUID_1.default.ExtendedStreamPropertiesObject;
|
||||
/**
|
||||
* 4.7 Metadata Object (optional, 0 or 1)
|
||||
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/04_objects_in_the_asf_header_extension_object.html#4_7
|
||||
*/
|
||||
class MetadataObjectState extends State {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
get(buf, off) {
|
||||
const tags = [];
|
||||
const descriptionRecordsCount = buf.readUInt16LE(off);
|
||||
let pos = off + 2;
|
||||
for (let i = 0; i < descriptionRecordsCount; i += 1) {
|
||||
pos += 4;
|
||||
const nameLen = buf.readUInt16LE(pos);
|
||||
pos += 2;
|
||||
const dataType = buf.readUInt16LE(pos);
|
||||
pos += 2;
|
||||
const dataLen = buf.readUInt32LE(pos);
|
||||
pos += 4;
|
||||
const name = AsfUtil_1.AsfUtil.parseUnicodeAttr(buf.slice(pos, pos + nameLen));
|
||||
pos += nameLen;
|
||||
const data = buf.slice(pos, pos + dataLen);
|
||||
pos += dataLen;
|
||||
const parseAttr = AsfUtil_1.AsfUtil.getParserForAttr(dataType);
|
||||
if (!parseAttr) {
|
||||
throw new Error('unexpected value headerType: ' + dataType);
|
||||
}
|
||||
this.postProcessTag(tags, name, dataType, data);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
exports.MetadataObjectState = MetadataObjectState;
|
||||
MetadataObjectState.guid = GUID_1.default.MetadataObject;
|
||||
// 4.8 Metadata Library Object (optional, 0 or 1)
|
||||
class MetadataLibraryObjectState extends MetadataObjectState {
|
||||
constructor(header) {
|
||||
super(header);
|
||||
}
|
||||
}
|
||||
exports.MetadataLibraryObjectState = MetadataLibraryObjectState;
|
||||
MetadataLibraryObjectState.guid = GUID_1.default.MetadataLibraryObject;
|
||||
/**
|
||||
* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd757977(v=vs.85).aspx
|
||||
*/
|
||||
class WmPictureToken {
|
||||
constructor(len) {
|
||||
this.len = len;
|
||||
}
|
||||
static fromBase64(base64str) {
|
||||
return this.fromBuffer(Buffer.from(base64str, 'base64'));
|
||||
}
|
||||
static fromBuffer(buffer) {
|
||||
const pic = new WmPictureToken(buffer.length);
|
||||
return pic.get(buffer, 0);
|
||||
}
|
||||
get(buffer, offset) {
|
||||
const typeId = buffer.readUInt8(offset++);
|
||||
const size = buffer.readInt32LE(offset);
|
||||
let index = 5;
|
||||
while (buffer.readUInt16BE(index) !== 0) {
|
||||
index += 2;
|
||||
}
|
||||
const format = buffer.slice(5, index).toString('utf16le');
|
||||
while (buffer.readUInt16BE(index) !== 0) {
|
||||
index += 2;
|
||||
}
|
||||
const description = buffer.slice(5, index).toString('utf16le');
|
||||
return {
|
||||
type: ID3v2Token_1.AttachedPictureType[typeId],
|
||||
format,
|
||||
description,
|
||||
size,
|
||||
data: buffer.slice(index + 4)
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.WmPictureToken = WmPictureToken;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { BasicParser } from '../common/BasicParser';
|
||||
/**
|
||||
* Windows Media Metadata Usage Guidelines
|
||||
* Ref: https://msdn.microsoft.com/en-us/library/ms867702.aspx
|
||||
*
|
||||
* Ref:
|
||||
* https://tools.ietf.org/html/draft-fleischman-asf-01
|
||||
* https://hwiegman.home.xs4all.nl/fileformats/asf/ASF_Specification.pdf
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/index.html
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ee663575(v=vs.85).aspx
|
||||
*/
|
||||
export declare class AsfParser extends BasicParser {
|
||||
parse(): Promise<void>;
|
||||
private parseObjectHeader;
|
||||
private addTags;
|
||||
private parseExtensionObject;
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AsfParser = void 0;
|
||||
const type_1 = require("../type");
|
||||
const GUID_1 = require("./GUID");
|
||||
const AsfObject = require("./AsfObject");
|
||||
const _debug = require("debug");
|
||||
const BasicParser_1 = require("../common/BasicParser");
|
||||
const debug = _debug('music-metadata:parser:ASF');
|
||||
const headerType = 'asf';
|
||||
/**
|
||||
* Windows Media Metadata Usage Guidelines
|
||||
* Ref: https://msdn.microsoft.com/en-us/library/ms867702.aspx
|
||||
*
|
||||
* Ref:
|
||||
* https://tools.ietf.org/html/draft-fleischman-asf-01
|
||||
* https://hwiegman.home.xs4all.nl/fileformats/asf/ASF_Specification.pdf
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/index.html
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ee663575(v=vs.85).aspx
|
||||
*/
|
||||
class AsfParser extends BasicParser_1.BasicParser {
|
||||
async parse() {
|
||||
const header = await this.tokenizer.readToken(AsfObject.TopLevelHeaderObjectToken);
|
||||
if (!header.objectId.equals(GUID_1.default.HeaderObject)) {
|
||||
throw new Error('expected asf header; but was not found; got: ' + header.objectId.str);
|
||||
}
|
||||
try {
|
||||
await this.parseObjectHeader(header.numberOfHeaderObjects);
|
||||
}
|
||||
catch (err) {
|
||||
debug('Error while parsing ASF: %s', err);
|
||||
}
|
||||
}
|
||||
async parseObjectHeader(numberOfObjectHeaders) {
|
||||
let tags;
|
||||
do {
|
||||
// Parse common header of the ASF Object (3.1)
|
||||
const header = await this.tokenizer.readToken(AsfObject.HeaderObjectToken);
|
||||
// Parse data part of the ASF Object
|
||||
debug('header GUID=%s', header.objectId.str);
|
||||
switch (header.objectId.str) {
|
||||
case AsfObject.FilePropertiesObject.guid.str: // 3.2
|
||||
const fpo = await this.tokenizer.readToken(new AsfObject.FilePropertiesObject(header));
|
||||
this.metadata.setFormat('duration', Number(fpo.playDuration / BigInt(1000)) / 10000 - Number(fpo.preroll) / 1000);
|
||||
this.metadata.setFormat('bitrate', fpo.maximumBitrate);
|
||||
break;
|
||||
case AsfObject.StreamPropertiesObject.guid.str: // 3.3
|
||||
const spo = await this.tokenizer.readToken(new AsfObject.StreamPropertiesObject(header));
|
||||
this.metadata.setFormat('container', 'ASF/' + spo.streamType);
|
||||
break;
|
||||
case AsfObject.HeaderExtensionObject.guid.str: // 3.4
|
||||
const extHeader = await this.tokenizer.readToken(new AsfObject.HeaderExtensionObject());
|
||||
await this.parseExtensionObject(extHeader.extensionDataSize);
|
||||
break;
|
||||
case AsfObject.ContentDescriptionObjectState.guid.str: // 3.10
|
||||
tags = await this.tokenizer.readToken(new AsfObject.ContentDescriptionObjectState(header));
|
||||
this.addTags(tags);
|
||||
break;
|
||||
case AsfObject.ExtendedContentDescriptionObjectState.guid.str: // 3.11
|
||||
tags = await this.tokenizer.readToken(new AsfObject.ExtendedContentDescriptionObjectState(header));
|
||||
this.addTags(tags);
|
||||
break;
|
||||
case GUID_1.default.CodecListObject.str:
|
||||
const codecs = await AsfObject.readCodecEntries(this.tokenizer);
|
||||
codecs.forEach(codec => {
|
||||
this.metadata.addStreamInfo({
|
||||
type: codec.type.videoCodec ? type_1.TrackType.video : type_1.TrackType.audio,
|
||||
codecName: codec.codecName
|
||||
});
|
||||
});
|
||||
const audioCodecs = codecs.filter(codec => codec.type.audioCodec).map(codec => codec.codecName).join('/');
|
||||
this.metadata.setFormat('codec', audioCodecs);
|
||||
break;
|
||||
case GUID_1.default.StreamBitratePropertiesObject.str:
|
||||
// ToDo?
|
||||
await this.tokenizer.ignore(header.objectSize - AsfObject.HeaderObjectToken.len);
|
||||
break;
|
||||
case GUID_1.default.PaddingObject.str:
|
||||
// ToDo: register bytes pad
|
||||
debug('Padding: %s bytes', header.objectSize - AsfObject.HeaderObjectToken.len);
|
||||
await this.tokenizer.ignore(header.objectSize - AsfObject.HeaderObjectToken.len);
|
||||
break;
|
||||
default:
|
||||
this.metadata.addWarning('Ignore ASF-Object-GUID: ' + header.objectId.str);
|
||||
debug('Ignore ASF-Object-GUID: %s', header.objectId.str);
|
||||
await this.tokenizer.readToken(new AsfObject.IgnoreObjectState(header));
|
||||
}
|
||||
} while (--numberOfObjectHeaders);
|
||||
// done
|
||||
}
|
||||
addTags(tags) {
|
||||
tags.forEach(tag => {
|
||||
this.metadata.addTag(headerType, tag.id, tag.value);
|
||||
});
|
||||
}
|
||||
async parseExtensionObject(extensionSize) {
|
||||
do {
|
||||
// Parse common header of the ASF Object (3.1)
|
||||
const header = await this.tokenizer.readToken(AsfObject.HeaderObjectToken);
|
||||
const remaining = header.objectSize - AsfObject.HeaderObjectToken.len;
|
||||
// Parse data part of the ASF Object
|
||||
switch (header.objectId.str) {
|
||||
case AsfObject.ExtendedStreamPropertiesObjectState.guid.str: // 4.1
|
||||
// ToDo: extended stream header properties are ignored
|
||||
await this.tokenizer.readToken(new AsfObject.ExtendedStreamPropertiesObjectState(header));
|
||||
break;
|
||||
case AsfObject.MetadataObjectState.guid.str: // 4.7
|
||||
const moTags = await this.tokenizer.readToken(new AsfObject.MetadataObjectState(header));
|
||||
this.addTags(moTags);
|
||||
break;
|
||||
case AsfObject.MetadataLibraryObjectState.guid.str: // 4.8
|
||||
const mlTags = await this.tokenizer.readToken(new AsfObject.MetadataLibraryObjectState(header));
|
||||
this.addTags(mlTags);
|
||||
break;
|
||||
case GUID_1.default.PaddingObject.str:
|
||||
// ToDo: register bytes pad
|
||||
await this.tokenizer.ignore(remaining);
|
||||
break;
|
||||
case GUID_1.default.CompatibilityObject.str:
|
||||
this.tokenizer.ignore(remaining);
|
||||
break;
|
||||
case GUID_1.default.ASF_Index_Placeholder_Object.str:
|
||||
await this.tokenizer.ignore(remaining);
|
||||
break;
|
||||
default:
|
||||
this.metadata.addWarning('Ignore ASF-Object-GUID: ' + header.objectId.str);
|
||||
// console.log("Ignore ASF-Object-GUID: %s", header.objectId.str);
|
||||
await this.tokenizer.readToken(new AsfObject.IgnoreObjectState(header));
|
||||
break;
|
||||
}
|
||||
extensionSize -= header.objectSize;
|
||||
} while (extensionSize > 0);
|
||||
}
|
||||
}
|
||||
exports.AsfParser = AsfParser;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { CommonTagMapper } from '../common/GenericTagMapper';
|
||||
import { IRating, ITag } from '../type';
|
||||
export declare class AsfTagMapper extends CommonTagMapper {
|
||||
static toRating(rating: string): IRating;
|
||||
constructor();
|
||||
protected postMap(tag: ITag): void;
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AsfTagMapper = void 0;
|
||||
const GenericTagMapper_1 = require("../common/GenericTagMapper");
|
||||
/**
|
||||
* ASF Metadata tag mappings.
|
||||
* See http://msdn.microsoft.com/en-us/library/ms867702.aspx
|
||||
*/
|
||||
const asfTagMap = {
|
||||
Title: 'title',
|
||||
Author: 'artist',
|
||||
'WM/AlbumArtist': 'albumartist',
|
||||
'WM/AlbumTitle': 'album',
|
||||
'WM/Year': 'date',
|
||||
'WM/OriginalReleaseTime': 'originaldate',
|
||||
'WM/OriginalReleaseYear': 'originalyear',
|
||||
Description: 'comment',
|
||||
'WM/TrackNumber': 'track',
|
||||
'WM/PartOfSet': 'disk',
|
||||
'WM/Genre': 'genre',
|
||||
'WM/Composer': 'composer',
|
||||
'WM/Lyrics': 'lyrics',
|
||||
'WM/AlbumSortOrder': 'albumsort',
|
||||
'WM/TitleSortOrder': 'titlesort',
|
||||
'WM/ArtistSortOrder': 'artistsort',
|
||||
'WM/AlbumArtistSortOrder': 'albumartistsort',
|
||||
'WM/ComposerSortOrder': 'composersort',
|
||||
'WM/Writer': 'lyricist',
|
||||
'WM/Conductor': 'conductor',
|
||||
'WM/ModifiedBy': 'remixer',
|
||||
'WM/Engineer': 'engineer',
|
||||
'WM/Producer': 'producer',
|
||||
'WM/DJMixer': 'djmixer',
|
||||
'WM/Mixer': 'mixer',
|
||||
'WM/Publisher': 'label',
|
||||
'WM/ContentGroupDescription': 'grouping',
|
||||
'WM/SubTitle': 'subtitle',
|
||||
'WM/SetSubTitle': 'discsubtitle',
|
||||
// 'WM/PartOfSet': 'totaldiscs',
|
||||
'WM/IsCompilation': 'compilation',
|
||||
'WM/SharedUserRating': 'rating',
|
||||
'WM/BeatsPerMinute': 'bpm',
|
||||
'WM/Mood': 'mood',
|
||||
'WM/Media': 'media',
|
||||
'WM/CatalogNo': 'catalognumber',
|
||||
'MusicBrainz/Album Status': 'releasestatus',
|
||||
'MusicBrainz/Album Type': 'releasetype',
|
||||
'MusicBrainz/Album Release Country': 'releasecountry',
|
||||
'WM/Script': 'script',
|
||||
'WM/Language': 'language',
|
||||
Copyright: 'copyright',
|
||||
LICENSE: 'license',
|
||||
'WM/EncodedBy': 'encodedby',
|
||||
'WM/EncodingSettings': 'encodersettings',
|
||||
'WM/Barcode': 'barcode',
|
||||
'WM/ISRC': 'isrc',
|
||||
'MusicBrainz/Track Id': 'musicbrainz_recordingid',
|
||||
'MusicBrainz/Release Track Id': 'musicbrainz_trackid',
|
||||
'MusicBrainz/Album Id': 'musicbrainz_albumid',
|
||||
'MusicBrainz/Artist Id': 'musicbrainz_artistid',
|
||||
'MusicBrainz/Album Artist Id': 'musicbrainz_albumartistid',
|
||||
'MusicBrainz/Release Group Id': 'musicbrainz_releasegroupid',
|
||||
'MusicBrainz/Work Id': 'musicbrainz_workid',
|
||||
'MusicBrainz/TRM Id': 'musicbrainz_trmid',
|
||||
'MusicBrainz/Disc Id': 'musicbrainz_discid',
|
||||
'Acoustid/Id': 'acoustid_id',
|
||||
'Acoustid/Fingerprint': 'acoustid_fingerprint',
|
||||
'MusicIP/PUID': 'musicip_puid',
|
||||
'WM/ARTISTS': 'artists',
|
||||
'WM/InitialKey': 'key',
|
||||
ASIN: 'asin',
|
||||
'WM/Work': 'work',
|
||||
'WM/AuthorURL': 'website',
|
||||
'WM/Picture': 'picture'
|
||||
};
|
||||
class AsfTagMapper extends GenericTagMapper_1.CommonTagMapper {
|
||||
static toRating(rating) {
|
||||
return {
|
||||
rating: parseFloat(rating + 1) / 5
|
||||
};
|
||||
}
|
||||
constructor() {
|
||||
super(['asf'], asfTagMap);
|
||||
}
|
||||
postMap(tag) {
|
||||
switch (tag.id) {
|
||||
case 'WM/SharedUserRating':
|
||||
const keys = tag.id.split(':');
|
||||
tag.value = AsfTagMapper.toRating(tag.value);
|
||||
tag.id = keys[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.AsfTagMapper = AsfTagMapper;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/// <reference types="node" />
|
||||
import { DataType } from "./AsfObject";
|
||||
export declare type AttributeParser = (buf: Buffer) => boolean | string | number | bigint | Buffer;
|
||||
export declare class AsfUtil {
|
||||
static getParserForAttr(i: DataType): AttributeParser;
|
||||
static parseUnicodeAttr(buf: any): string;
|
||||
private static attributeParsers;
|
||||
private static parseByteArrayAttr;
|
||||
private static parseBoolAttr;
|
||||
private static parseDWordAttr;
|
||||
private static parseQWordAttr;
|
||||
private static parseWordAttr;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AsfUtil = void 0;
|
||||
const util = require("../common/Util");
|
||||
const Token = require("token-types");
|
||||
class AsfUtil {
|
||||
static getParserForAttr(i) {
|
||||
return AsfUtil.attributeParsers[i];
|
||||
}
|
||||
static parseUnicodeAttr(buf) {
|
||||
return util.stripNulls(util.decodeString(buf, 'utf16le'));
|
||||
}
|
||||
static parseByteArrayAttr(buf) {
|
||||
const newBuf = Buffer.alloc(buf.length);
|
||||
buf.copy(newBuf);
|
||||
return newBuf;
|
||||
}
|
||||
static parseBoolAttr(buf, offset = 0) {
|
||||
return AsfUtil.parseWordAttr(buf, offset) === 1;
|
||||
}
|
||||
static parseDWordAttr(buf, offset = 0) {
|
||||
return buf.readUInt32LE(offset);
|
||||
}
|
||||
static parseQWordAttr(buf, offset = 0) {
|
||||
return Token.UINT64_LE.get(buf, offset);
|
||||
}
|
||||
static parseWordAttr(buf, offset = 0) {
|
||||
return buf.readUInt16LE(offset);
|
||||
}
|
||||
}
|
||||
exports.AsfUtil = AsfUtil;
|
||||
AsfUtil.attributeParsers = [
|
||||
AsfUtil.parseUnicodeAttr,
|
||||
AsfUtil.parseByteArrayAttr,
|
||||
AsfUtil.parseBoolAttr,
|
||||
AsfUtil.parseDWordAttr,
|
||||
AsfUtil.parseQWordAttr,
|
||||
AsfUtil.parseWordAttr,
|
||||
AsfUtil.parseByteArrayAttr
|
||||
];
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/// <reference types="node" />
|
||||
/**
|
||||
* Ref:
|
||||
* https://tools.ietf.org/html/draft-fleischman-asf-01, Appendix A: ASF GUIDs
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/index.html
|
||||
*
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
||||
*
|
||||
* ASF File Structure:
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ee663575(v=vs.85).aspx
|
||||
*
|
||||
* ASF GUIDs:
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
||||
*
|
||||
* https://github.com/dji-sdk/FFmpeg/blob/master/libavformat/asf.c
|
||||
*/
|
||||
export default class GUID {
|
||||
str: string;
|
||||
static HeaderObject: GUID;
|
||||
static DataObject: GUID;
|
||||
static SimpleIndexObject: GUID;
|
||||
static IndexObject: GUID;
|
||||
static MediaObjectIndexObject: GUID;
|
||||
static TimecodeIndexObject: GUID;
|
||||
static FilePropertiesObject: GUID;
|
||||
static StreamPropertiesObject: GUID;
|
||||
static HeaderExtensionObject: GUID;
|
||||
static CodecListObject: GUID;
|
||||
static ScriptCommandObject: GUID;
|
||||
static MarkerObject: GUID;
|
||||
static BitrateMutualExclusionObject: GUID;
|
||||
static ErrorCorrectionObject: GUID;
|
||||
static ContentDescriptionObject: GUID;
|
||||
static ExtendedContentDescriptionObject: GUID;
|
||||
static ContentBrandingObject: GUID;
|
||||
static StreamBitratePropertiesObject: GUID;
|
||||
static ContentEncryptionObject: GUID;
|
||||
static ExtendedContentEncryptionObject: GUID;
|
||||
static DigitalSignatureObject: GUID;
|
||||
static PaddingObject: GUID;
|
||||
static ExtendedStreamPropertiesObject: GUID;
|
||||
static AdvancedMutualExclusionObject: GUID;
|
||||
static GroupMutualExclusionObject: GUID;
|
||||
static StreamPrioritizationObject: GUID;
|
||||
static BandwidthSharingObject: GUID;
|
||||
static LanguageListObject: GUID;
|
||||
static MetadataObject: GUID;
|
||||
static MetadataLibraryObject: GUID;
|
||||
static IndexParametersObject: GUID;
|
||||
static MediaObjectIndexParametersObject: GUID;
|
||||
static TimecodeIndexParametersObject: GUID;
|
||||
static CompatibilityObject: GUID;
|
||||
static AdvancedContentEncryptionObject: GUID;
|
||||
static AudioMedia: GUID;
|
||||
static VideoMedia: GUID;
|
||||
static CommandMedia: GUID;
|
||||
static JFIF_Media: GUID;
|
||||
static Degradable_JPEG_Media: GUID;
|
||||
static FileTransferMedia: GUID;
|
||||
static BinaryMedia: GUID;
|
||||
static ASF_Index_Placeholder_Object: GUID;
|
||||
static fromBin(bin: Buffer, offset?: number): GUID;
|
||||
/**
|
||||
* Decode GUID in format like "B503BF5F-2EA9-CF11-8EE3-00C00C205365"
|
||||
* @param objectId Binary GUID
|
||||
* @param offset Read offset in bytes, default 0
|
||||
* @returns {string} GUID as dashed hexadecimal representation
|
||||
*/
|
||||
static decode(objectId: Buffer, offset?: number): string;
|
||||
/**
|
||||
* Decode stream type
|
||||
* @param {string} mediaType
|
||||
* @returns {string}
|
||||
*/
|
||||
static decodeMediaType(mediaType: GUID): string;
|
||||
/**
|
||||
* Encode GUID
|
||||
* @param guid GUID like: "B503BF5F-2EA9-CF11-8EE3-00C00C205365"
|
||||
* @returns {Buffer} Encoded Bnary GUID
|
||||
*/
|
||||
static encode(str: string): Buffer;
|
||||
constructor(str: string);
|
||||
equals(guid: GUID): boolean;
|
||||
toBin(): Buffer;
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Ref:
|
||||
* https://tools.ietf.org/html/draft-fleischman-asf-01, Appendix A: ASF GUIDs
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/index.html
|
||||
*
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
||||
*
|
||||
* ASF File Structure:
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ee663575(v=vs.85).aspx
|
||||
*
|
||||
* ASF GUIDs:
|
||||
* http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
||||
*
|
||||
* https://github.com/dji-sdk/FFmpeg/blob/master/libavformat/asf.c
|
||||
*/
|
||||
class GUID {
|
||||
constructor(str) {
|
||||
this.str = str;
|
||||
}
|
||||
static fromBin(bin, offset = 0) {
|
||||
return new GUID(this.decode(bin, offset));
|
||||
}
|
||||
/**
|
||||
* Decode GUID in format like "B503BF5F-2EA9-CF11-8EE3-00C00C205365"
|
||||
* @param objectId Binary GUID
|
||||
* @param offset Read offset in bytes, default 0
|
||||
* @returns {string} GUID as dashed hexadecimal representation
|
||||
*/
|
||||
static decode(objectId, offset = 0) {
|
||||
const guid = objectId.readUInt32LE(offset).toString(16) + "-" +
|
||||
objectId.readUInt16LE(offset + 4).toString(16) + "-" +
|
||||
objectId.readUInt16LE(offset + 6).toString(16) + "-" +
|
||||
objectId.readUInt16BE(offset + 8).toString(16) + "-" +
|
||||
objectId.slice(offset + 10, offset + 16).toString('hex');
|
||||
return guid.toUpperCase();
|
||||
}
|
||||
/**
|
||||
* Decode stream type
|
||||
* @param {string} mediaType
|
||||
* @returns {string}
|
||||
*/
|
||||
static decodeMediaType(mediaType) {
|
||||
switch (mediaType.str) {
|
||||
case GUID.AudioMedia.str: return 'audio';
|
||||
case GUID.VideoMedia.str: return 'video';
|
||||
case GUID.CommandMedia.str: return 'command';
|
||||
case GUID.Degradable_JPEG_Media.str: return 'degradable-jpeg';
|
||||
case GUID.FileTransferMedia.str: return 'file-transfer';
|
||||
case GUID.BinaryMedia.str: return 'binary';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Encode GUID
|
||||
* @param guid GUID like: "B503BF5F-2EA9-CF11-8EE3-00C00C205365"
|
||||
* @returns {Buffer} Encoded Bnary GUID
|
||||
*/
|
||||
static encode(str) {
|
||||
const bin = Buffer.alloc(16);
|
||||
bin.writeUInt32LE(parseInt(str.slice(0, 8), 16), 0);
|
||||
bin.writeUInt16LE(parseInt(str.slice(9, 13), 16), 4);
|
||||
bin.writeUInt16LE(parseInt(str.slice(14, 18), 16), 6);
|
||||
Buffer.from(str.slice(19, 23), "hex").copy(bin, 8);
|
||||
Buffer.from(str.slice(24), "hex").copy(bin, 10);
|
||||
return bin;
|
||||
}
|
||||
equals(guid) {
|
||||
return this.str === guid.str;
|
||||
}
|
||||
toBin() {
|
||||
return GUID.encode(this.str);
|
||||
}
|
||||
}
|
||||
exports.default = GUID;
|
||||
// 10.1 Top-level ASF object GUIDs
|
||||
GUID.HeaderObject = new GUID("75B22630-668E-11CF-A6D9-00AA0062CE6C");
|
||||
GUID.DataObject = new GUID("75B22636-668E-11CF-A6D9-00AA0062CE6C");
|
||||
GUID.SimpleIndexObject = new GUID("33000890-E5B1-11CF-89F4-00A0C90349CB");
|
||||
GUID.IndexObject = new GUID("D6E229D3-35DA-11D1-9034-00A0C90349BE");
|
||||
GUID.MediaObjectIndexObject = new GUID("FEB103F8-12AD-4C64-840F-2A1D2F7AD48C");
|
||||
GUID.TimecodeIndexObject = new GUID("3CB73FD0-0C4A-4803-953D-EDF7B6228F0C");
|
||||
// 10.2 Header Object GUIDs
|
||||
GUID.FilePropertiesObject = new GUID("8CABDCA1-A947-11CF-8EE4-00C00C205365");
|
||||
GUID.StreamPropertiesObject = new GUID("B7DC0791-A9B7-11CF-8EE6-00C00C205365");
|
||||
GUID.HeaderExtensionObject = new GUID("5FBF03B5-A92E-11CF-8EE3-00C00C205365");
|
||||
GUID.CodecListObject = new GUID("86D15240-311D-11D0-A3A4-00A0C90348F6");
|
||||
GUID.ScriptCommandObject = new GUID("1EFB1A30-0B62-11D0-A39B-00A0C90348F6");
|
||||
GUID.MarkerObject = new GUID("F487CD01-A951-11CF-8EE6-00C00C205365");
|
||||
GUID.BitrateMutualExclusionObject = new GUID("D6E229DC-35DA-11D1-9034-00A0C90349BE");
|
||||
GUID.ErrorCorrectionObject = new GUID("75B22635-668E-11CF-A6D9-00AA0062CE6C");
|
||||
GUID.ContentDescriptionObject = new GUID("75B22633-668E-11CF-A6D9-00AA0062CE6C");
|
||||
GUID.ExtendedContentDescriptionObject = new GUID("D2D0A440-E307-11D2-97F0-00A0C95EA850");
|
||||
GUID.ContentBrandingObject = new GUID("2211B3FA-BD23-11D2-B4B7-00A0C955FC6E");
|
||||
GUID.StreamBitratePropertiesObject = new GUID("7BF875CE-468D-11D1-8D82-006097C9A2B2");
|
||||
GUID.ContentEncryptionObject = new GUID("2211B3FB-BD23-11D2-B4B7-00A0C955FC6E");
|
||||
GUID.ExtendedContentEncryptionObject = new GUID("298AE614-2622-4C17-B935-DAE07EE9289C");
|
||||
GUID.DigitalSignatureObject = new GUID("2211B3FC-BD23-11D2-B4B7-00A0C955FC6E");
|
||||
GUID.PaddingObject = new GUID("1806D474-CADF-4509-A4BA-9AABCB96AAE8");
|
||||
// 10.3 Header Extension Object GUIDs
|
||||
GUID.ExtendedStreamPropertiesObject = new GUID("14E6A5CB-C672-4332-8399-A96952065B5A");
|
||||
GUID.AdvancedMutualExclusionObject = new GUID("A08649CF-4775-4670-8A16-6E35357566CD");
|
||||
GUID.GroupMutualExclusionObject = new GUID("D1465A40-5A79-4338-B71B-E36B8FD6C249");
|
||||
GUID.StreamPrioritizationObject = new GUID("D4FED15B-88D3-454F-81F0-ED5C45999E24");
|
||||
GUID.BandwidthSharingObject = new GUID("A69609E6-517B-11D2-B6AF-00C04FD908E9");
|
||||
GUID.LanguageListObject = new GUID("7C4346A9-EFE0-4BFC-B229-393EDE415C85");
|
||||
GUID.MetadataObject = new GUID("C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA");
|
||||
GUID.MetadataLibraryObject = new GUID("44231C94-9498-49D1-A141-1D134E457054");
|
||||
GUID.IndexParametersObject = new GUID("D6E229DF-35DA-11D1-9034-00A0C90349BE");
|
||||
GUID.MediaObjectIndexParametersObject = new GUID("6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7");
|
||||
GUID.TimecodeIndexParametersObject = new GUID("F55E496D-9797-4B5D-8C8B-604DFE9BFB24");
|
||||
GUID.CompatibilityObject = new GUID("26F18B5D-4584-47EC-9F5F-0E651F0452C9");
|
||||
GUID.AdvancedContentEncryptionObject = new GUID("43058533-6981-49E6-9B74-AD12CB86D58C");
|
||||
// 10.4 Stream Properties Object Stream Type GUIDs
|
||||
GUID.AudioMedia = new GUID("F8699E40-5B4D-11CF-A8FD-00805F5C442B");
|
||||
GUID.VideoMedia = new GUID("BC19EFC0-5B4D-11CF-A8FD-00805F5C442B");
|
||||
GUID.CommandMedia = new GUID("59DACFC0-59E6-11D0-A3AC-00A0C90348F6");
|
||||
GUID.JFIF_Media = new GUID("B61BE100-5B4E-11CF-A8FD-00805F5C442B");
|
||||
GUID.Degradable_JPEG_Media = new GUID("35907DE0-E415-11CF-A917-00805F5C442B");
|
||||
GUID.FileTransferMedia = new GUID("91BD222C-F21C-497A-8B6D-5AA86BFC0185");
|
||||
GUID.BinaryMedia = new GUID("3AFB65E2-47EF-40F2-AC2C-70A90D71D343");
|
||||
GUID.ASF_Index_Placeholder_Object = new GUID("D9AADE20-7C17-4F9C-BC28-8555DD98E2A2");
|
||||
Reference in New Issue
Block a user