New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

jv4-demuxer

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jv4-demuxer - npm Package Compare versions

Comparing version 1.0.2 to 1.0.3

5

package.json
{
"name": "jv4-demuxer",
"version": "1.0.2",
"version": "1.0.3",
"description": "",

@@ -15,4 +15,5 @@ "type": "module",

"afsm": "^2.2.3",
"eventemitter3": "^4.0.7"
"eventemitter3": "^4.0.7",
"oput": "^1.2.2"
}
}

40

src/base.ts
import { EventEmitter } from "eventemitter3";
import Oput from "oput";
export const enum DemuxEvent {

@@ -6,19 +7,40 @@ AUDIO_ENCODER_CONFIG_CHANGED = "audio-encoder-config-changed",

}
export const enum DemuxMode {
PULL,
PUSH
}
export interface Source {
oput?: Oput;
read<T extends number | Uint8Array>(need: T): Promise<Uint8Array>;
}
export abstract class BaseDemuxer extends EventEmitter {
constructor(
public source: {
read<T extends number | Uint8Array>(need: T): Promise<Uint8Array>;
}
public mode: DemuxMode = DemuxMode.PULL,
public source?: Source
) {
super();
if (source) {
if (mode == DemuxMode.PULL) {
this.startPull(source);
} else {
source.oput = new Oput(this.demux());
}
}
}
audioReadable: ReadableStream<EncodedAudioChunkInit> = new ReadableStream({
pull: async (controller) => controller.enqueue(await this.pullAudio()),
});
videoReadable: ReadableStream<EncodedVideoChunkInit> = new ReadableStream({
pull: async (controller) => controller.enqueue(await this.pullVideo()),
});
audioReadable?: ReadableStream<EncodedAudioChunkInit>;
videoReadable?: ReadableStream<EncodedVideoChunkInit>;
audioEncoderConfig?: AudioEncoderConfig;
videoEncoderConfig?: VideoEncoderConfig;
abstract pull(): Promise<void>;
startPull(source: Source) {
this.mode = DemuxMode.PULL;
this.source = source;
this.audioReadable = new ReadableStream({
pull: async (controller) => controller.enqueue(await this.pullAudio()),
});
this.videoReadable = new ReadableStream({
pull: async (controller) => controller.enqueue(await this.pullVideo()),
});
}
abstract demux(): Generator<number, void, Uint8Array>;
gotAudio?: (data: EncodedAudioChunkInit) => void;

@@ -25,0 +47,0 @@ gotVideo?: (data: EncodedVideoChunkInit) => void;

@@ -1,2 +0,2 @@

import { BaseDemuxer, DemuxEvent } from "./base";
import { BaseDemuxer, DemuxEvent, DemuxMode } from "./base";

@@ -13,3 +13,5 @@ export interface FlvTag {

header?: Uint8Array;
async readTag(): Promise<{
tmp8 = new Uint8Array(4);
dv = new DataView(this.tmp8.buffer);
async pullTag(): Promise<{
type: number;

@@ -20,24 +22,14 @@ data: Uint8Array;

const t = new Uint8Array(15); //复用15个字节,前面4个字节是上一个tag的长度,跳过
const tmp8 = new Uint8Array(4);
const dv = new DataView(tmp8.buffer);
this.readTag = async () => {
await this.source.read(t);
this.pullTag = async () => {
await this.source!.read(t);
const type = t[4]; //tag类型,8是音频,9是视频,18是script
tmp8[0] = 0; //首位置空,上一次读取可能会有残留数据
tmp8.set(t.subarray(5, 8), 1);
const length = dv.getUint32(0); //大端方式读取长度
tmp8.set(t.subarray(8, 11), 1);
let timestamp = dv.getUint32(0); //大端方式读取时间戳
if (timestamp === 0xffffff) {
//扩展时间戳
tmp8[0] = t[11]; //最高位
timestamp = dv.getUint32(0);
}
const data = await this.source.read(length);
const length = this.readLength(t.subarray(5, 8));
const timestamp = this.readTimestamp(t.subarray(8, 11));
const data = await this.source!.read(length);
return { type, data: data.slice(), timestamp };
};
console.time("flv");
await this.source.read(9).then((data) => {
await this.source!.read(9).then((data) => {
this.header = data;
console.log(data)
console.log(data);
if (

@@ -52,67 +44,120 @@ data[0] != "F".charCodeAt(0) ||

});
return this.readTag();
return this.pullTag();
}
async pull(): Promise<void> {
const value = await this.readTag();
if (value) {
switch (value.type) {
case 8:
if (!this.audioEncoderConfig) {
this.audioEncoderConfig = {
codec:
{ 10: "aac", 7: "pcma", 8: "pcmu" }[value.data[0] >> 4] ||
"unknown",
numberOfChannels: 1,
sampleRate: 44100,
};
//TODO: parse audio config
if (this.audioEncoderConfig.codec == "aac") {
}
}
readTag(data: Uint8Array) {
const type = data[0]; //tag类型,8是音频,9是视频,18是script
const length = this.readLength(data.subarray(1, 4));
const timestamp = this.readTimestamp(data.subarray(4, 8));
this.gotTag(type, data.subarray(11, 11 + length), timestamp);
}
gotTag(type: number, data: Uint8Array, timestamp: number) {
switch (type) {
case 8:
if (!this.audioEncoderConfig) {
this.audioEncoderConfig = {
codec:
{ 10: "aac", 7: "pcma", 8: "pcmu" }[data[0] >> 4] ||
"unknown",
numberOfChannels: 1,
sampleRate: 44100,
};
//TODO: parse audio config
if (this.audioEncoderConfig.codec == "aac") {
if (value.data[1] == 0x00) {
this.emit(
DemuxEvent.AUDIO_ENCODER_CONFIG_CHANGED,
value.data.subarray(2)
);
return this.pull();
}
}
return this.gotAudio!({
type: "key",
data:
this.audioEncoderConfig.codec == "aac"
? value.data.subarray(2)
: value.data.subarray(1),
timestamp: value.timestamp,
duration: 0,
});
case 9:
if (!this.videoEncoderConfig) {
this.videoEncoderConfig = {
codec:
{ 7: "avc", 12: "hevc" }[value.data[0] & 0xf] || "unknown",
width: 0,
height: 0,
};
//TODO: parse video config
}
if (value.data[1] == 0x00) {
this.emit(
DemuxEvent.VIDEO_ENCODER_CONFIG_CHANGED,
value.data.subarray(5)
);
}
if (this.audioEncoderConfig.codec == "aac" && data[1] == 0x00) {
this.emit(
DemuxEvent.AUDIO_ENCODER_CONFIG_CHANGED,
data.subarray(2)
);
if (this.mode == DemuxMode.PULL)
return this.pull();
}
return this.gotVideo!({
type: value.data[0] >> 4 == 1 ? "key" : "delta",
data: value.data.subarray(5),
timestamp: value.timestamp,
duration: 0,
});
default:
else return;
}
return this.gotAudio?.({
type: "key",
data:
this.audioEncoderConfig.codec == "aac"
? data.subarray(2)
: data.subarray(1),
timestamp: timestamp,
duration: 0,
});
case 9:
if (!this.videoEncoderConfig) {
this.videoEncoderConfig = {
codec:
{ 7: "avc", 12: "hevc" }[data[0] & 0xf] || "unknown",
width: 0,
height: 0,
};
//TODO: parse video config
}
if (data[1] == 0x00) {
this.emit(
DemuxEvent.VIDEO_ENCODER_CONFIG_CHANGED,
data.subarray(5)
);
if (this.mode == DemuxMode.PULL)
return this.pull();
else return;
}
return this.gotVideo?.({
type: data[0] >> 4 == 1 ? "key" : "delta",
data: data.subarray(5),
timestamp: timestamp,
duration: 0,
});
default:
if (this.mode == DemuxMode.PULL)
return this.pull();
}
}
}
async pull(): Promise<void> {
const value = await this.pullTag();
if (value) {
return this.gotTag(value.type, value.data, value.timestamp);
}
}
readLength(data: Uint8Array) {
this.tmp8[0] = 0; //首位置空,上一次读取可能会有残留数据
this.tmp8.set(data, 1);
return this.dv.getUint32(0); //大端方式读取长度
}
readTimestamp(data: Uint8Array) {
this.tmp8.set(data.subarray(0, 3), 1);
let timestamp = this.dv.getUint32(0); //大端方式读取时间戳
if (timestamp === 0xffffff) {
//扩展时间戳
this.tmp8[0] = data[3]; //最高位
timestamp = this.dv.getUint32(0);
}
return timestamp;
}
readHead(data: Uint8Array) {
console.time("flv");
this.header = data;
console.log(data);
if (
data[0] != "F".charCodeAt(0) ||
data[1] != "L".charCodeAt(0) ||
data[2] != "V".charCodeAt(0)
) {
throw new Error("not flv");
}
console.timeEnd("flv");
}
*demux(): Generator<number, void, Uint8Array> {
this.readHead(yield 13);
while (true) {
let data = yield 11;
const type = data[0]; //tag类型,8是音频,9是视频,18是script
const length = this.readLength(data.subarray(1, 4));
const timestamp = this.readTimestamp(data.subarray(4, 8));
data = yield length;
this.gotTag(type, data.slice(), timestamp);
yield 4;
}
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc