@vkontakte/videoplayer-core 2.0.181-dev.820dcf4a0.0 → 2.0.181-dev.b9d1527b2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es2015.cjs +8 -8
- package/es2015.esm.js +8 -8
- package/esnext.cjs +8 -8
- package/esnext.esm.js +15 -15
- package/evergreen.esm.js +8 -8
- package/package.json +2 -2
- package/types/player/types.d.ts +2 -2
- package/types/providers/DashProvider/lib/buffer.d.ts +28 -3
- package/types/providers/DashProvider/lib/fetcher.d.ts +31 -6
- package/types/providers/DashProvider/lib/player.d.ts +2 -0
- package/types/providers/DashProviderVirtual/lib/buffer/types.d.ts +1 -0
- package/types/providers/DashProviderVirtual/lib/buffer/virtualBuffer/baseVirtualBufferManager.d.ts +7 -3
- package/types/providers/DashProviderVirtual/lib/fetcher.d.ts +30 -6
- package/types/providers/DashProviderVirtual/lib/player/basePlayer.d.ts +1 -0
- package/types/providers/DashProviderVirtual/lib/player/types.d.ts +2 -1
- package/types/utils/ThroughputEstimator.d.ts +20 -4
- package/types/utils/tuningConfig.d.ts +48 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vkontakte/videoplayer-core",
|
|
3
|
-
"version": "2.0.181-dev.
|
|
3
|
+
"version": "2.0.181-dev.b9d1527b2.0",
|
|
4
4
|
"author": "vk.ru",
|
|
5
5
|
"description": "Videoplayer core library based on the vk.ru platform",
|
|
6
6
|
"homepage": "https://vk.ru",
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"**/*.d.ts"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@vkontakte/videoplayer-shared": "1.0.110-dev.
|
|
45
|
+
"@vkontakte/videoplayer-shared": "1.0.110-dev.b9d1527b2.0"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/types/player/types.d.ts
CHANGED
|
@@ -227,7 +227,7 @@ export interface IPlayerEvents {
|
|
|
227
227
|
*/
|
|
228
228
|
fetcherRecoverableError$: IObservable<IError>;
|
|
229
229
|
/**
|
|
230
|
-
* Произошел
|
|
230
|
+
* Произошел серьёзный/долгий stall.
|
|
231
231
|
*/
|
|
232
232
|
severeStallOccured$: IObservable<boolean>;
|
|
233
233
|
}
|
|
@@ -350,7 +350,7 @@ export interface PlayerInfoValues {
|
|
|
350
350
|
*/
|
|
351
351
|
isBuffering$: boolean;
|
|
352
352
|
/**
|
|
353
|
-
* Состояние
|
|
353
|
+
* Состояние stall. Возникает по истечении stallIgnoreThreshold после старта нежелательной буферизации
|
|
354
354
|
* Статус только нежелательной буферизации возникает при опустошении буфера в процессе воспроизведения
|
|
355
355
|
* и не возникает при начальном наполнении буфера или после перемотки
|
|
356
356
|
*/
|
|
@@ -12,6 +12,7 @@ export interface Dependencies {
|
|
|
12
12
|
compatibilityMode?: boolean;
|
|
13
13
|
manifest: Manifest | null;
|
|
14
14
|
tracer: ITracer;
|
|
15
|
+
getSeekTargetPosition?: () => Milliseconds | undefined;
|
|
15
16
|
}
|
|
16
17
|
export interface Gap extends IRange<Milliseconds> {
|
|
17
18
|
representation: Representation["id"];
|
|
@@ -56,6 +57,7 @@ export declare class BufferManager {
|
|
|
56
57
|
private destroyAbortController;
|
|
57
58
|
private useSmartRepresentationSwitch;
|
|
58
59
|
private readonly getCurrentPosition;
|
|
60
|
+
private readonly getSeekTargetPosition;
|
|
59
61
|
private readonly isActiveLowLatency;
|
|
60
62
|
private tuning;
|
|
61
63
|
private forwardBufferTarget;
|
|
@@ -77,9 +79,8 @@ export declare class BufferManager {
|
|
|
77
79
|
private index;
|
|
78
80
|
private lastDataObtainedTimestampMs;
|
|
79
81
|
private loadByteRangeSegmentsTimeoutId;
|
|
80
|
-
private preDownloadedFirstSegment;
|
|
81
82
|
private brokenSegmentAppendDetected;
|
|
82
|
-
constructor(kind: StreamKind, mediaSource: MediaSource, representations: Representation[], { fetcher, tuning, getCurrentPosition, isActiveLowLatency, compatibilityMode, manifest, tracer }: Dependencies);
|
|
83
|
+
constructor(kind: StreamKind, mediaSource: MediaSource, representations: Representation[], { fetcher, tuning, getCurrentPosition, getSeekTargetPosition, isActiveLowLatency, compatibilityMode, manifest, tracer }: Dependencies);
|
|
83
84
|
startWith: ReturnType<typeof abortable<[Representation["id"]]>>;
|
|
84
85
|
switchToWithPreviousAbort(newRepresentationId: Representation["id"], dropBuffer?: boolean): Promise<void | undefined>;
|
|
85
86
|
switchTo: ReturnType<typeof abortable<[Representation["id"], boolean | undefined], void>>;
|
|
@@ -108,7 +109,6 @@ export declare class BufferManager {
|
|
|
108
109
|
private loadSegments;
|
|
109
110
|
private loadTemplateSegment;
|
|
110
111
|
private updateRepresentationsBaseUrlIfNeeded;
|
|
111
|
-
private feedPreDownloadedFirstSegment;
|
|
112
112
|
private loadByteRangeSegments;
|
|
113
113
|
private prepareByteRangeFetchSegmentParams;
|
|
114
114
|
private buildCacheMetadata;
|
|
@@ -128,7 +128,32 @@ export declare class BufferManager {
|
|
|
128
128
|
private detectBrokenSegmentAppend;
|
|
129
129
|
private abortSegment;
|
|
130
130
|
private loadNextInit;
|
|
131
|
+
private isFirstSegmentPreloadEnabled;
|
|
132
|
+
private isStartPositionWithinFirstSegment;
|
|
133
|
+
/**
|
|
134
|
+
* Работа с прелоадом первого сегмента.
|
|
135
|
+
*
|
|
136
|
+
* Загружает init+index репрезентации и применяет их через onInitFetched (результаты сетятся в initData /
|
|
137
|
+
* parsedInitData / segments). Резолвится после применения инита.
|
|
138
|
+
*
|
|
139
|
+
* Режимы вызова:
|
|
140
|
+
* - critical=true — только startWith (стартовая загрузка);
|
|
141
|
+
* - critical=false — switchTo и ленивые idle-догрузки остальных репрезентаций.
|
|
142
|
+
*
|
|
143
|
+
* С прелоадом первого сегмента (critical && isFirstSegmentPreloadEnabled):
|
|
144
|
+
* - фетчер делает один combined-запрос init+index+первый сегмент;
|
|
145
|
+
* - инит применяется досрочно из onInitFetched, до скачанных байтов сегмента, и сразу ставится
|
|
146
|
+
* init-аппенд в SourceBufferTaskQueue — раньше кормления чанков сегмента (кормление синхронно
|
|
147
|
+
* внутри onProgress, поэтому без этого сегмент аппендился бы раньше инита);
|
|
148
|
+
* - резолв loadInit ждёт завершения init-аппенда;
|
|
149
|
+
* - чанки сегмента кормятся через handleFirstSegmentChunk, хвост добивается в finalizeCombinedStreaming
|
|
150
|
+
*/
|
|
131
151
|
private loadInit;
|
|
152
|
+
/**
|
|
153
|
+
* Здесь мы только финалим хвостовую часть — сегментную — комбинированного запроса.
|
|
154
|
+
* Если сегмент не докачался не беда, loadSegments подхватит.
|
|
155
|
+
*/
|
|
156
|
+
private finalizeCombinedStreaming;
|
|
132
157
|
private dropBuffer;
|
|
133
158
|
private pruneBuffer;
|
|
134
159
|
private abortBuffer;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { HttpConnectionType, HttpConnectionMetrics, HttpDownloadMetrics } from "../../../player/types";
|
|
2
2
|
import type ThroughputEstimator from "../../../utils/ThroughputEstimator";
|
|
3
|
-
import type { Byte, IValueSubject, ISubject, Milliseconds, IRange, ITracer, IError } from "@vkontakte/videoplayer-shared";
|
|
3
|
+
import type { Byte, ExactVideoQuality, IValueSubject, ISubject, Milliseconds, IRange, ITracer, IError } from "@vkontakte/videoplayer-shared";
|
|
4
4
|
import { abortable } from "@vkontakte/videoplayer-shared";
|
|
5
|
-
import type { ExactVideoQuality } from "@vkontakte/videoplayer-shared";
|
|
6
5
|
import type { CommonInit, GenericContainerParser, Segment, SegmentReference } from "../../utils/parsers/types";
|
|
7
6
|
import type { ISegmentCacheStorage, CacheMetadata, SegmentCacheMode } from "../../utils/segmentCache";
|
|
8
7
|
declare global {
|
|
@@ -50,13 +49,19 @@ export interface FetchParams {
|
|
|
50
49
|
ignoreNetworkErrors?: boolean;
|
|
51
50
|
representationHeight?: number;
|
|
52
51
|
cacheMetadata?: CacheMetadata;
|
|
52
|
+
firstSegmentPreload?: boolean;
|
|
53
|
+
onInitFetched?: (data: IRepresentationInitReady) => void;
|
|
54
|
+
onSegmentsDone?: (result: ICombinedBodyResult | null) => void;
|
|
53
55
|
}
|
|
54
|
-
export
|
|
56
|
+
export interface IRepresentationInitReady {
|
|
55
57
|
init: CommonInit | null;
|
|
56
58
|
segments: Segment[];
|
|
59
|
+
initData: ArrayBuffer;
|
|
60
|
+
globalFrom: Byte;
|
|
61
|
+
}
|
|
62
|
+
export interface ICombinedBodyResult {
|
|
57
63
|
dataView: DataView;
|
|
58
|
-
|
|
59
|
-
};
|
|
64
|
+
}
|
|
60
65
|
export declare class Fetcher {
|
|
61
66
|
private throughputEstimator;
|
|
62
67
|
private requestQuic;
|
|
@@ -97,11 +102,31 @@ export declare class Fetcher {
|
|
|
97
102
|
private trackRequestStart;
|
|
98
103
|
fetchManifest: ReturnType<typeof abortable<[string], string | null>>;
|
|
99
104
|
fetch: ReturnType<typeof abortable<[string, FetchParams], ArrayBuffer | null>>;
|
|
100
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Резолвится, когда получили init данные.
|
|
107
|
+
*/
|
|
108
|
+
fetchRepresentation(segmentReference: SegmentReference, parser: GenericContainerParser<unknown>, fetchParams: FetchParams): Promise<IRepresentationInitReady | null>;
|
|
101
109
|
/** Скипнутые сегменты не записаны в кэш — при следующем запросе региона докачаются как miss. */
|
|
102
110
|
private traceSkippedCacheSegments;
|
|
103
111
|
destroy(): void;
|
|
112
|
+
/**
|
|
113
|
+
* Тут проверяем расположение данных, нам из манифеста приходит только длина сегмента, ничто не известно про оффсет от начала,
|
|
114
|
+
* заявлено, что первый сегмент идет сразу после индекса. Проверяем контракт: true — нарушен, первый сегмент лежит не сразу за индексом.
|
|
115
|
+
*/
|
|
116
|
+
private isFirstSegmentLayoutViolated;
|
|
117
|
+
private logFirstSegmentLayoutViolation;
|
|
118
|
+
/**
|
|
119
|
+
* Адаптируем onProgress комбинированного запроса: отдельно информируем о данных инита
|
|
120
|
+
* и о данных сегмента на основании одного combined-стрима.
|
|
121
|
+
*/
|
|
122
|
+
private createInitAndFirstSegmentStreamingOnProgress;
|
|
104
123
|
private fetchByteRangeRepresentation;
|
|
124
|
+
private fetchByteRangeRepresentationWithFirstSegment;
|
|
125
|
+
/**
|
|
126
|
+
* Если происходит downloadSignal то мы не хотим абортить загрузку инит части запроса,
|
|
127
|
+
* скачиваем нужную часть запроса, как только инит+индекс готов, сегмент хвост можно абортить.
|
|
128
|
+
*/
|
|
129
|
+
private createSegmentTailAbort;
|
|
105
130
|
private fetchTemplateRepresentation;
|
|
106
131
|
private isLikelyCacheHitByTtfb;
|
|
107
132
|
private doFetch;
|
|
@@ -22,6 +22,7 @@ export interface Params {
|
|
|
22
22
|
tracer: ITracer;
|
|
23
23
|
isOnDemand: boolean;
|
|
24
24
|
dashMaxTvVideoQuality: Nullable<ExactVideoQuality>;
|
|
25
|
+
getSeekTargetPosition?: () => Milliseconds | undefined;
|
|
25
26
|
}
|
|
26
27
|
export declare class Player {
|
|
27
28
|
private element;
|
|
@@ -89,6 +90,7 @@ export declare class Player {
|
|
|
89
90
|
private liveWasInterrupted;
|
|
90
91
|
private isOnDemand;
|
|
91
92
|
private dashMaxTvVideoQuality;
|
|
93
|
+
private getSeekTargetPosition;
|
|
92
94
|
private destroyController;
|
|
93
95
|
constructor(params: Params);
|
|
94
96
|
initManifest: ReturnType<typeof abortable<[HTMLMediaElement, string, number]>>;
|
|
@@ -20,6 +20,7 @@ export interface Dependencies {
|
|
|
20
20
|
getCurrentPosition(): Milliseconds | undefined;
|
|
21
21
|
getCurrentStallDuration(): Milliseconds;
|
|
22
22
|
isActiveLowLatency(): boolean;
|
|
23
|
+
getSeekTargetPosition?(): Milliseconds | undefined;
|
|
23
24
|
}
|
|
24
25
|
export interface IVirtualBufferManager<T extends Segment = Segment> {
|
|
25
26
|
error$: ISubject<IError>;
|
package/types/providers/DashProviderVirtual/lib/buffer/virtualBuffer/baseVirtualBufferManager.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Fetcher, FetchParamsWithUrl, Priority
|
|
1
|
+
import type { Fetcher, FetchParamsWithUrl, Priority } from "../../fetcher";
|
|
2
2
|
import type { CommonInit, ContainerParser, Representation, Segment } from "../../../../utils/parsers/types";
|
|
3
3
|
import { StreamKind } from "../../../../utils/parsers/types";
|
|
4
4
|
import type { ITuningConfig } from "../../../../../utils/tuningConfig";
|
|
@@ -26,7 +26,7 @@ export declare abstract class BaseVirtualBufferManager<T extends Segment> implem
|
|
|
26
26
|
protected downloadingRepresentationId: Representation["id"] | undefined;
|
|
27
27
|
protected switchingRepresentationId: Representation["id"] | null;
|
|
28
28
|
protected initData: Map<Representation["id"], ArrayBuffer>;
|
|
29
|
-
protected initDataPromises: Map<Representation["id"], Promise<
|
|
29
|
+
protected initDataPromises: Map<Representation["id"], Promise<unknown>>;
|
|
30
30
|
protected idleCallbacks: Map<Representation["id"], number>;
|
|
31
31
|
protected parsedInitData: Map<Representation["id"], CommonInit>;
|
|
32
32
|
protected forwardBufferRepresentations: Map<Representation["id"], T[]>;
|
|
@@ -55,8 +55,9 @@ export declare abstract class BaseVirtualBufferManager<T extends Segment> implem
|
|
|
55
55
|
protected subscription: Subscription;
|
|
56
56
|
protected readonly getCurrentPosition: () => Milliseconds | undefined;
|
|
57
57
|
protected readonly getCurrentStallDuration: () => Milliseconds | undefined;
|
|
58
|
+
protected readonly getSeekTargetPosition: (() => Milliseconds | undefined) | undefined;
|
|
58
59
|
protected brokenSegmentAppendDetected: boolean;
|
|
59
|
-
protected constructor(kind: StreamKind, nativeBufferManager: NativeBufferManager, representations: Representation[], { fetcher, tracer, tuning, getCurrentPosition, getCurrentStallDuration, manifest, segmentCacheService }: Dependencies);
|
|
60
|
+
protected constructor(kind: StreamKind, nativeBufferManager: NativeBufferManager, representations: Representation[], { fetcher, tracer, tuning, getCurrentPosition, getCurrentStallDuration, getSeekTargetPosition, manifest, segmentCacheService }: Dependencies);
|
|
60
61
|
protected abstract loadItems(itemsToLoad: IBufferPlaybackQueueItem<T>[], representation: Representation, priority?: Priority): Promise<void>;
|
|
61
62
|
protected abstract selectItemsToLoad(): IBufferPlaybackQueueItem<T>[];
|
|
62
63
|
protected abstract prepareFetchParams(items: IBufferPlaybackQueueItem<T>[], representation: Representation): FetchParamsWithUrl;
|
|
@@ -110,6 +111,9 @@ export declare abstract class BaseVirtualBufferManager<T extends Segment> implem
|
|
|
110
111
|
protected loadInits(): Promise<void>;
|
|
111
112
|
protected loadInitIfNeeded(representation: Representation, requestedPriority?: Priority, critical?: boolean): Promise<void>;
|
|
112
113
|
protected loadInit(representation: Representation, requestedPriority?: Priority, critical?: boolean): Promise<void>;
|
|
114
|
+
private isStartPositionWithinFirstSegment;
|
|
115
|
+
private finalizeCombinedStreaming;
|
|
116
|
+
private resetStreamingItem;
|
|
113
117
|
protected maintainNativeBuffer(signal: AbortSignal): Promise<void>;
|
|
114
118
|
protected maintainPlaybackBuffer(position: Milliseconds): Promise<void>;
|
|
115
119
|
protected actualizeLastSegmentInfo(currentPosition: Milliseconds): void;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { HttpConnectionType, HttpConnectionMetrics, HttpDownloadMetrics } from "../../../player/types";
|
|
2
2
|
import type ThroughputEstimator from "../../../utils/ThroughputEstimator";
|
|
3
|
-
import type { Byte, IError, IRange, ISubject, ITracer, IValueSubject, Milliseconds } from "@vkontakte/videoplayer-shared";
|
|
3
|
+
import type { Byte, ExactVideoQuality, IError, IRange, ISubject, ITracer, IValueSubject, Milliseconds } from "@vkontakte/videoplayer-shared";
|
|
4
4
|
import { abortable } from "@vkontakte/videoplayer-shared";
|
|
5
|
-
import type { ExactVideoQuality } from "@vkontakte/videoplayer-shared";
|
|
6
5
|
import type { CommonInit, GenericContainerParser, Segment, SegmentReference, StreamKind } from "../../utils/parsers/types";
|
|
7
6
|
import type { ISegmentCacheStorage, CacheMetadata, SegmentCacheMode } from "../../utils/segmentCache";
|
|
8
7
|
declare global {
|
|
@@ -44,17 +43,30 @@ export interface FetchParams {
|
|
|
44
43
|
onProgressTasks?: Promise<void>[];
|
|
45
44
|
signal?: AbortSignal;
|
|
46
45
|
priority?: Priority;
|
|
46
|
+
representationHeight?: number;
|
|
47
47
|
measureThroughput?: boolean;
|
|
48
48
|
isLowLatency?: boolean;
|
|
49
49
|
bufferOptimisation?: boolean;
|
|
50
50
|
kind: StreamKind;
|
|
51
51
|
cacheMetadata?: CacheMetadata;
|
|
52
|
+
firstSegmentPreload?: boolean;
|
|
53
|
+
onInitFetched?: (data: IRepresentationInitReady) => void;
|
|
54
|
+
onSegmentsDone?: (result: ICombinedBodyResult | null) => void;
|
|
55
|
+
}
|
|
56
|
+
export interface IRepresentationInitReady {
|
|
57
|
+
init: CommonInit | null;
|
|
58
|
+
segments: Segment[];
|
|
59
|
+
initData: ArrayBuffer;
|
|
60
|
+
globalFrom: Byte;
|
|
61
|
+
}
|
|
62
|
+
export interface ICombinedBodyResult {
|
|
63
|
+
dataView: DataView;
|
|
52
64
|
}
|
|
53
65
|
export type RepresentationFetchResult<T extends Segment> = {
|
|
54
|
-
|
|
55
|
-
initDataView: DataView;
|
|
66
|
+
init: CommonInit | null;
|
|
56
67
|
segments: T[];
|
|
57
|
-
|
|
68
|
+
initData: ArrayBuffer;
|
|
69
|
+
globalFrom: Byte;
|
|
58
70
|
};
|
|
59
71
|
export declare class Fetcher {
|
|
60
72
|
private throughputEstimator;
|
|
@@ -96,11 +108,23 @@ export declare class Fetcher {
|
|
|
96
108
|
private trackRequestStart;
|
|
97
109
|
fetchManifest: ReturnType<typeof abortable<[string], string | null>>;
|
|
98
110
|
fetch: ReturnType<typeof abortable<[string, FetchParams], ArrayBuffer | null>>;
|
|
99
|
-
fetchRepresentation<T extends Segment>(segmentReference: SegmentReference, parser: GenericContainerParser<unknown>,
|
|
111
|
+
fetchRepresentation<T extends Segment>(segmentReference: SegmentReference, parser: GenericContainerParser<unknown>, fetchParams?: FetchParams): Promise<RepresentationFetchResult<T> | null | undefined>;
|
|
100
112
|
/** Скипнутые сегменты не записаны в кэш — при следующем запросе региона докачаются как miss. */
|
|
101
113
|
private traceSkippedCacheSegments;
|
|
102
114
|
destroy(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Так как качем инит+сегмент, то нам нужно раньше сообщить, что мы получили инит+индекс часть,
|
|
117
|
+
* а сегментная пусть качается дальше.
|
|
118
|
+
*/
|
|
119
|
+
private createInitAndFirstSegmentStreamingOnProgress;
|
|
120
|
+
/**
|
|
121
|
+
* Проверяем, что данные сегмента идут сразу после индекса.
|
|
122
|
+
*/
|
|
123
|
+
private isFirstSegmentLayoutViolated;
|
|
124
|
+
private logFirstSegmentLayoutViolation;
|
|
103
125
|
private fetchByteRangeRepresentation;
|
|
126
|
+
private fetchByteRangeRepresentationWithFirstSegment;
|
|
127
|
+
private createSegmentTailAbort;
|
|
104
128
|
private fetchTemplateRepresentation;
|
|
105
129
|
private isLikelyCacheHitByTtfb;
|
|
106
130
|
private doFetch;
|
|
@@ -80,6 +80,7 @@ export declare abstract class BasePlayer {
|
|
|
80
80
|
updateDurationError$: Subject<IError>;
|
|
81
81
|
private isOnDemand;
|
|
82
82
|
private dashMaxTvVideoQuality;
|
|
83
|
+
private getSeekTargetPosition;
|
|
83
84
|
protected constructor(params: Params);
|
|
84
85
|
protected abstract prepareManifestUrlString(manifestBaseUrlString: string, offset: number): string;
|
|
85
86
|
protected abstract setSourceInitDuration(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type ThroughputEstimator from "../../../../utils/ThroughputEstimator";
|
|
2
2
|
import type { ITuningConfig } from "../../../../utils/tuningConfig";
|
|
3
3
|
import type { VideoCodec } from "../../../../player/types";
|
|
4
|
-
import type { ExactVideoQuality, ITracer, Nullable } from "@vkontakte/videoplayer-shared";
|
|
4
|
+
import type { ExactVideoQuality, ITracer, Milliseconds, Nullable } from "@vkontakte/videoplayer-shared";
|
|
5
5
|
export declare enum State {
|
|
6
6
|
NONE = "none",
|
|
7
7
|
MANIFEST_READY = "manifest_ready",
|
|
@@ -16,4 +16,5 @@ export interface Params {
|
|
|
16
16
|
tracer: ITracer;
|
|
17
17
|
isOnDemand?: boolean;
|
|
18
18
|
dashMaxTvVideoQuality?: Nullable<ExactVideoQuality>;
|
|
19
|
+
getSeekTargetPosition?: () => Milliseconds | undefined;
|
|
19
20
|
}
|
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
import type { Byte, Kbps, Milliseconds, IValueSubject } from "@vkontakte/videoplayer-shared";
|
|
1
|
+
import type { Byte, Kbps, Milliseconds, IValueSubject, IComponentLogger } from "@vkontakte/videoplayer-shared";
|
|
2
2
|
import type { ITuningConfig } from "./tuningConfig";
|
|
3
3
|
declare class ThroughputEstimator {
|
|
4
4
|
private throughput;
|
|
5
5
|
private rtt;
|
|
6
6
|
private subscription;
|
|
7
|
+
private wiring;
|
|
8
|
+
private readonly throughputVarianceProxy$;
|
|
9
|
+
private currentHost;
|
|
10
|
+
private hasThroughputSamples;
|
|
11
|
+
private hasRttSamples;
|
|
12
|
+
private storageReadDone;
|
|
7
13
|
private tuningConfig;
|
|
14
|
+
private readonly logger?;
|
|
15
|
+
private initialThroughputSource;
|
|
16
|
+
private initialRttSource;
|
|
8
17
|
private concurrentDownloads;
|
|
9
18
|
throughput$: IValueSubject<Kbps>;
|
|
10
19
|
rtt$: IValueSubject<Milliseconds>;
|
|
11
20
|
rttAdjustedThroughput$: IValueSubject<Kbps>;
|
|
12
|
-
constructor(tuning: ITuningConfig["throughputEstimator"]);
|
|
21
|
+
constructor(tuning: ITuningConfig["throughputEstimator"], hostname$?: IValueSubject<string | undefined>, logger?: IComponentLogger);
|
|
22
|
+
private static readLegacyNumber;
|
|
23
|
+
private static resolveInitialThroughput;
|
|
24
|
+
private static resolveInitialRtt;
|
|
25
|
+
private tryAdopt;
|
|
26
|
+
private recreateSmoothed;
|
|
27
|
+
private wireSmoothed;
|
|
13
28
|
destroy(): void;
|
|
14
29
|
trackXHR(xhr: XMLHttpRequest): void;
|
|
15
30
|
trackStream(stream: ReadableStream, isLowLatency?: boolean): void;
|
|
@@ -21,10 +36,11 @@ declare class ThroughputEstimator {
|
|
|
21
36
|
*/
|
|
22
37
|
addRawThroughput(rate: Kbps): void;
|
|
23
38
|
addRawRtt(time: Milliseconds): void;
|
|
39
|
+
private markSample;
|
|
24
40
|
private sanityCheck;
|
|
25
41
|
get throughputVariance$(): IValueSubject<number>;
|
|
26
|
-
private static
|
|
27
|
-
private
|
|
42
|
+
private static readHostRecord;
|
|
43
|
+
private writeStored;
|
|
28
44
|
private static validateStored;
|
|
29
45
|
}
|
|
30
46
|
export default ThroughputEstimator;
|
|
@@ -42,6 +42,15 @@ export type ITuningConfig = {
|
|
|
42
42
|
initialThroughput: Kbps;
|
|
43
43
|
measureNonSegmentRequests: boolean;
|
|
44
44
|
smallSampleMinDuration: Milliseconds;
|
|
45
|
+
/**
|
|
46
|
+
* Верхняя граница скорости одного замера (Кбпс), выше которой sanityCheck его
|
|
47
|
+
* отбрасывает: такую полосу не даёт пользовательский канал — это кэш CDN/браузера,
|
|
48
|
+
* отдавший объект практически мгновенно. Дефолт 1 000 000 Кбит/с унаследован из
|
|
49
|
+
* хардкода sanityCheck до этого поля. Поднимается в e2e (UVP-11954): CI-раннеры
|
|
50
|
+
* стоят в одном ДЦ с CDN и качают сегменты быстрее гигабита — с дефолтным лимитом
|
|
51
|
+
* все замеры режутся и хостовая запись кеша не появляется.
|
|
52
|
+
*/
|
|
53
|
+
maxSampleRate: Kbps;
|
|
45
54
|
cacheThresholdSize: Milliseconds;
|
|
46
55
|
/**
|
|
47
56
|
* UVP-11541: верхняя граница порога, ниже которого ответ считается кэш-хитом по TTFB.
|
|
@@ -71,7 +80,7 @@ export type ITuningConfig = {
|
|
|
71
80
|
* мелкий сегмент качается параллельно с аудио и другого замера не получает.
|
|
72
81
|
* Параллельной считается загрузка, отдававшая байты внутри окна замера: открытая,
|
|
73
82
|
* но молчащая (подвисла либо уже выкачала своё и ждёт done) полосу не занимает.
|
|
74
|
-
* Честный кэш (мгновенная отдача
|
|
83
|
+
* Честный кэш (мгновенная отдача — скорость замера выше maxSampleRate) по-прежнему режется.
|
|
75
84
|
*
|
|
76
85
|
* Пункты 3 и 4 добавлены после эксперимента 37621: без них включённые быстрые сэмплы
|
|
77
86
|
* систематически занижали оценку, ABR держал низкое качество и сегменты оставались
|
|
@@ -90,23 +99,41 @@ export type ITuningConfig = {
|
|
|
90
99
|
maxLastEvaluationTimeout: Milliseconds;
|
|
91
100
|
};
|
|
92
101
|
/**
|
|
93
|
-
*
|
|
94
|
-
* При включении: если сохранённое значение старше storedThroughputTtlMs
|
|
95
|
-
* или тип сети изменился (Wi-Fi→3G) — значение отбрасывается.
|
|
96
|
-
* Предотвращает старт в 4K при переходе с Wi-Fi на мобильную сеть.
|
|
102
|
+
* UVP-11954: кеш оценки сети с разрезом по хосту раздачи (CDN).
|
|
97
103
|
*
|
|
98
|
-
* При
|
|
99
|
-
*
|
|
100
|
-
*
|
|
104
|
+
* При включении: ключи vk_uvp_throughput / vk_uvp_rtt хранят JSON
|
|
105
|
+
* {"hosts":{"<host>":{"value","timestamp","networkType"?}}} — чтение только
|
|
106
|
+
* записи текущего хоста (TTL + совпадение networkType), промах = холодный старт
|
|
107
|
+
* (browser estimation, затем initialThroughput). Запись — hosts-only, без общей
|
|
108
|
+
* записи; набор хостов ограничен LRU по hostsLimit. Чтение отложено до
|
|
109
|
+
* первого события адаптации: определённая эмиссия hostname$ или первый замер.
|
|
101
110
|
*/
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
cdnCache: {
|
|
112
|
+
/**
|
|
113
|
+
* Гейт всех изменений UVP-11954. При false (дефолт): бит-в-бит прежнее
|
|
114
|
+
* поведение — plain number в legacy-ключах one_video_*; ключи vk_uvp_*
|
|
115
|
+
* не читаются и не пишутся. Это даёт безопасный rollback JS-версии
|
|
116
|
+
* без потери свежих данных для старого кода.
|
|
117
|
+
*/
|
|
118
|
+
enabled: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Срок жизни каждой хостовой записи кеша (мс). По умолчанию 4 часа.
|
|
121
|
+
* Запись старше TTL при чтении отбрасывается — холодный старт.
|
|
122
|
+
* Применимо только при cdnCache.enabled=true.
|
|
123
|
+
*/
|
|
124
|
+
ttlMs: Milliseconds;
|
|
125
|
+
/**
|
|
126
|
+
* Предел числа хостовых записей в hosts (UVP-11954). При превышении при записи
|
|
127
|
+
* вытесняется запись с наименьшим timestamp (не считая текущего хоста).
|
|
128
|
+
* Протухшие по TTL записи уходят первыми — отдельной чистки нет.
|
|
129
|
+
* 10: ключ кеша — хост раздачи, а балансировщик video.get выдаёт хост
|
|
130
|
+
* при каждой загрузке видео и не гарантирует повтор предыдущего. За TTL
|
|
131
|
+
* активный пользователь может собрать записи по нескольким хостам —
|
|
132
|
+
* 10 покрывает это с запасом при ~100 Б на запись, ~1 КБ на набор из 10.
|
|
133
|
+
* Применимо только при cdnCache.enabled=true.
|
|
134
|
+
*/
|
|
135
|
+
hostsLimit: number;
|
|
136
|
+
};
|
|
110
137
|
};
|
|
111
138
|
autoTrackSelection: {
|
|
112
139
|
bitrateFactorAtEmptyBuffer: number;
|
|
@@ -283,6 +310,8 @@ export type ITuningConfig = {
|
|
|
283
310
|
*/
|
|
284
311
|
recoverStuckSegmentLoading: boolean;
|
|
285
312
|
skipReloadOfFullyBufferedPartiallyEjectedSegments: boolean;
|
|
313
|
+
/** В Safari ждёт завершения append init-сегмента после abort перед выполнением remove. */
|
|
314
|
+
deferRemoveUntilAbortCompletes: boolean;
|
|
286
315
|
};
|
|
287
316
|
dashCmafLive: {
|
|
288
317
|
externalStopControl: boolean;
|
|
@@ -373,10 +402,10 @@ export type ITuningConfig = {
|
|
|
373
402
|
*/
|
|
374
403
|
stallIgnoreThreshold: Milliseconds;
|
|
375
404
|
/**
|
|
376
|
-
* Стратегия записи
|
|
405
|
+
* Стратегия записи stall
|
|
377
406
|
* verbose - записываем всё
|
|
378
|
-
* exclude-threshold - записываем
|
|
379
|
-
* include-threshold - записываем
|
|
407
|
+
* exclude-threshold - записываем stall длиннее stallIgnoreThreshold, но вычитаем длину stallIgnoreThreshold
|
|
408
|
+
* include-threshold - записываем stall длиннее stallIgnoreThreshold
|
|
380
409
|
*/
|
|
381
410
|
stallHandlingStrategy: "verbose" | "exclude-threshold" | "include-threshold";
|
|
382
411
|
stallIgnoreOnPause: boolean;
|